diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1e82a6e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+# Forge
+out/
+cache/
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..caa0ab1
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "lib/ds-test"]
+ path = lib/ds-test
+ url = https://github.com/dapphub/ds-test
+[submodule "lib/openzeppelin-contracts"]
+ path = lib/openzeppelin-contracts
+ url = https://github.com/OpenZeppelin/openzeppelin-contracts
diff --git a/2021/sep/proposal.sol b/2021/sep/proposal.sol
deleted file mode 100644
index 9d009b3..0000000
--- a/2021/sep/proposal.sol
+++ /dev/null
@@ -1 +0,0 @@
-// pragma solidity ^0.7.0;
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f80a188
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,677 @@
+All files included in this repo are under the following license except where
+otherwise specified.
+
+ 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)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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:
+
+ 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
+.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..707f8e8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+# Load `.env` file and export variables.
+-include .env
+
+# Test
+test:; forge test -f https://fee7372b6e224441b747bf1fde15b2bd.eth.rpc.rivet.cloud --sender 0xEC3281124d4c2FCA8A88e3076C1E7749CfEcb7F2 --tx-origin 0xEC3281124d4c2FCA8A88e3076C1E7749CfEcb7F2 --verbosity 3
diff --git a/README.md b/README.md
index e1140a1..05df495 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,22 @@
-# onchain
-Yam On Chain Proposals
+# Yam On Chain Proposals
+
+**[Triggered Proposals](https://yam.finance/#/governance)**
+
+## Development
+
+For the development we use the [foundry](https://github.com/gakonst/foundry) toolkit.
+
+```sh
+# Install rustup.
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+```
+
+```sh
+# Install the foundry binary.
+cargo install --git https://github.com/gakonst/foundry --bin forge --locked
+```
+
+```sh
+# Run forked network tests.
+make test
+```
diff --git a/lib/ds-test b/lib/ds-test
new file mode 160000
index 0000000..0a5da56
--- /dev/null
+++ b/lib/ds-test
@@ -0,0 +1 @@
+Subproject commit 0a5da56b0d65960e6a994d2ec8245e6edd38c248
diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts
new file mode 160000
index 0000000..7d17acf
--- /dev/null
+++ b/lib/openzeppelin-contracts
@@ -0,0 +1 @@
+Subproject commit 7d17acfb2f12be0a2ad4c08ad4a3d823704b68d6
diff --git a/src/Proposal19.sol b/src/Proposal19.sol
new file mode 100644
index 0000000..f0b60d9
--- /dev/null
+++ b/src/Proposal19.sol
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+// Interfaces
+import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
+
+// Contracts
+import {YAMTokenInterface} from "./utils/YAMTokenInterface.sol";
+import {VestingPool} from "./utils/VestingPool.sol";
+
+interface YVault {
+ function deposit(uint256 amount, address recipient)
+ external
+ returns (uint256);
+
+ function withdraw(uint256 amount) external returns (uint256);
+}
+
+
+contract Proposal19 {
+ /// @notice Contracts for paying contributors in `USDC`.
+ address internal constant RESERVES = address(0x97990B693835da58A281636296D2Bf02787DEa17);
+ VestingPool internal constant pool = VestingPool(0xDCf613db29E4d0B35e7e15e93BF6cc6315eB0b82);
+ YVault internal constant yUSDC = YVault(0x5f18C75AbDAe578b483E5F43f12a39cF75b973a9);
+ IERC20 internal constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
+
+ function execute() public {
+ /// @dev Transfer the total yUSDC balance from `RESERVES` to `this` contract.
+ IERC20(address(yUSDC)).transferFrom(RESERVES, address(this), IERC20(address(yUSDC)).balanceOf(RESERVES));
+ /// @dev Withdraw `USDC` for `yUSDC`.
+ yUSDC.withdraw(type(uint256).max);
+
+ /**
+ * @notice Transfer `USDC` to contributors.
+ */
+
+ // E
+ USDC.transfer(
+ 0x8A8acf1cEcC4ed6Fe9c408449164CE2034AdC03f,
+ yearlyUSDToMonthlyUSD(120000 * (10**6))
+ );
+
+ // Nate
+ USDC.transfer(
+ 0xEC3281124d4c2FCA8A88e3076C1E7749CfEcb7F2,
+ yearlyUSDToMonthlyUSD(105000 * (10**6))
+ );
+
+ // Chilly
+ USDC.transfer(
+ 0x01e0C7b70E0E05a06c7cC8deeb97Fa03d6a77c9C,
+ yearlyUSDToMonthlyUSD(84000 * (10**6))
+ );
+
+ // Krguman
+ USDC.transfer(
+ 0xcc506b3c2967022094C3B00276617883167BF32B,
+ yearlyUSDToMonthlyUSD(30000 * (10**6))
+ );
+
+ // Designer
+ USDC.transfer(
+ 0x3FdcED6B5C1f176b543E5E0b841cB7224596C33C,
+ yearlyUSDToMonthlyUSD(96000 * (10**6))
+ );
+
+ // Ross
+ USDC.transfer(
+ 0x88c868B1024ECAefDc648eb152e91C57DeA984d0,
+ yearlyUSDToMonthlyUSD(84000 * (10**6))
+ );
+
+ // Jason
+ USDC.transfer(
+ 0x43fD74401B4BF04095590a5308B6A5e3Db44b9e3,
+ yearlyUSDToMonthlyUSD(48000 * (10**6))
+ );
+
+ // Blokku
+ USDC.transfer(
+ 0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3,
+ yearlyUSDToMonthlyUSD(22500 * (10**6))
+ );
+
+ // Kris
+ USDC.transfer(
+ 0x386568164bdC5B105a66D8Ae83785D4758939eE6,
+ yearlyUSDToMonthlyUSD(15000 * (10**6))
+ );
+
+ // Jono
+ USDC.transfer(
+ 0xFcB4f3a1710FefA583e7b003F3165f2E142bC725,
+ yearlyUSDToMonthlyUSD(42000 * (10**6))
+ );
+
+ // Will
+ USDC.transfer(
+ 0x31920DF2b31B5f7ecf65BDb2c497DE31d299d472,
+ yearlyUSDToMonthlyUSD(84000 * (10**6))
+ );
+
+ /// @dev Deposit the remainding `USDC` balance back into the `yUSDC` vault.
+ uint256 usdcBalance = USDC.balanceOf(address(this));
+ USDC.approve(address(yUSDC), usdcBalance);
+ yUSDC.deposit(usdcBalance, RESERVES);
+
+ /**
+ * @notice Setup `YAM` streams.
+ */
+
+ // Update Ross stream
+ pool.openStream(
+ 0x88c868B1024ECAefDc648eb152e91C57DeA984d0,
+ 180 days,
+ 1250 * (10**24) * 6
+ );
+ }
+
+ function yearlyUSDToMonthlyUSD(uint256 yearlyUSD) internal pure returns (uint256) {
+ return ((yearlyUSD / uint256(12)));
+ }
+}
diff --git a/src/test/Proposal19.t.sol b/src/test/Proposal19.t.sol
new file mode 100644
index 0000000..8212ee8
--- /dev/null
+++ b/src/test/Proposal19.t.sol
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+// Interfaces
+import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
+
+// Contracts
+import {Proposal19} from "../Proposal19.sol";
+import "../utils/YAMTest.sol";
+import {YAMDelegate3} from "../utils/YAMDelegate3.sol";
+
+
+// Proposal for July contributor payment and stream setup.
+contract Proposal19Test is YAMTest {
+ Proposal19 private proposal;
+
+ function setUp() public {
+ setUpYAMTest();
+ proposal = new Proposal19();
+ }
+
+ function testProposal19() public {
+ address[] memory targets = new address[](2);
+ uint256[] memory values = new uint256[](2);
+ string[] memory signatures = new string[](2);
+ bytes[] memory calldatas = new bytes[](2);
+ string memory description = "Setup proposol as sub gov on vestingPool, whitelist withdrawals for contributor payments.";
+
+ /// @notice Set proposal as sub gov for vesting pool.
+ targets[0] = address(vestingPool);
+ signatures[0] = "setSubGov(address,bool)";
+ calldatas[0] = abi.encode(address(proposal), true);
+
+ /// @notice Whitelist propogal to withdraw usdc.
+ targets[1] = address(reserves);
+ signatures[1] = "whitelistWithdrawals(address[],uint256[],address[])";
+ address[] memory whos = new address[](1);
+ uint256[] memory amounts = new uint256[](1);
+ address[] memory tokens = new address[](1);
+ whos[0] = address(proposal);
+ amounts[0] = type(uint256).max;
+ tokens[0] = address(yUSDC);
+ calldatas[1] = abi.encode(whos, amounts, tokens);
+
+ /// @notice Get quorum for test proposal.
+ getQuorum(yamDelegator, proposer);
+ vm.roll(block.number + 1);
+
+ /// @notice Post and vote on proposal.
+ rollProposal(targets, values, signatures, calldatas, description);
+
+ vm.roll(block.number + 1);
+ proposal.execute();
+ ff(61 minutes);
+
+ // Assert reserves have the yUSDC we should have.
+ assertTrue(IERC20(address(yUSDC)).balanceOf(address(reserves)) > 760000 * (10**6));
+
+ // Assert no USDC or yUSDC was left in the proposal.
+ assertEq(IERC20(USDC).balanceOf(address(proposal)), 0);
+ assertEq(IERC20(yUSDC).balanceOf(address(proposal)), 0);
+ }
+}
diff --git a/src/utils/VestingPool.sol b/src/utils/VestingPool.sol
new file mode 100644
index 0000000..c08c633
--- /dev/null
+++ b/src/utils/VestingPool.sol
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+// Contracts
+import {YAMDelegate3} from "./YAMDelegate3.sol";
+
+
+contract VestingPool {
+ struct Stream {
+ address recipient;
+ uint128 startTime;
+ uint128 length;
+ uint256 totalAmount;
+ uint256 amountPaidOut;
+ }
+
+ /**
+ * @notice Governor for this contract
+ */
+ address public gov;
+
+ /**
+ * @notice Pending governance for this contract
+ */
+ address public pendingGov;
+
+ /// @notice Mapping containing valid stream managers
+ mapping(address => bool) public isSubGov;
+
+ /// @notice Amount of tokens allocated to streams that hasn't yet been claimed
+ uint256 public totalUnclaimedInStreams;
+
+ /// @notice The number of streams created so far
+ uint256 public streamCount;
+
+ /// @notice All streams
+ mapping(uint256 => Stream) public streams;
+
+ /// @notice YAM token
+ YAMDelegate3 public yam;
+
+ /**
+ * @notice Event emitted when a sub gov is enabled/disabled
+ */
+ event SubGovModified(
+ address account,
+ bool isSubGov
+ );
+
+ /**
+ * @notice Event emitted when stream is opened
+ */
+ event StreamOpened(
+ address indexed account,
+ uint256 indexed streamId,
+ uint256 length,
+ uint256 totalAmount
+ );
+
+ /**
+ * @notice Event emitted when stream is closed
+ */
+ event StreamClosed(uint256 indexed streamId);
+
+ /**
+ * @notice Event emitted on payout
+ */
+ event Payout(
+ uint256 indexed streamId,
+ address indexed recipient,
+ uint256 amount
+ );
+
+ /**
+ * @notice Event emitted when pendingGov is changed
+ */
+ event NewPendingGov(
+ address oldPendingGov,
+ address newPendingGov
+ );
+
+ /**
+ * @notice Event emitted when gov is changed
+ */
+ event NewGov(
+ address oldGov,
+ address newGov
+ );
+
+ constructor(YAMDelegate3 _yam)
+ public
+ {
+ gov = msg.sender;
+ yam = _yam;
+ }
+
+ modifier onlyGov() {
+ require(msg.sender == gov, "VestingPool::onlyGov: account is not gov");
+ _;
+ }
+
+ modifier canManageStreams() {
+ require(
+ isSubGov[msg.sender] || (msg.sender == gov),
+ "VestingPool::canManageStreams: account cannot manage streams"
+ );
+ _;
+ }
+
+ /**
+ * @dev Set whether an account can open/close streams. Only callable by the current gov contract
+ * @param account The account to set permissions for.
+ * @param _isSubGov Whether or not this account can manage streams
+ */
+ function setSubGov(address account, bool _isSubGov)
+ public
+ onlyGov
+ {
+ isSubGov[account] = _isSubGov;
+ emit SubGovModified(account, _isSubGov);
+ }
+
+ /** @notice sets the pendingGov
+ * @param pendingGov_ The address of the contract to use for authentication.
+ */
+ function _setPendingGov(address pendingGov_)
+ external
+ onlyGov
+ {
+ address oldPendingGov = pendingGov;
+ pendingGov = pendingGov_;
+ emit NewPendingGov(oldPendingGov, pendingGov_);
+ }
+
+ /** @notice accepts governance over this contract
+ *
+ */
+ function _acceptGov()
+ external
+ {
+ require(msg.sender == pendingGov, "!pending");
+ address oldGov = gov;
+ gov = pendingGov;
+ pendingGov = address(0);
+ emit NewGov(oldGov, gov);
+ }
+
+ /**
+ * @dev Opens a new stream that continuously pays out.
+ * @param recipient Account that will receive the funds.
+ * @param length The amount of time in seconds that the stream lasts
+ * @param totalAmount The total amount to payout in the stream
+ */
+ function openStream(
+ address recipient,
+ uint128 length,
+ uint256 totalAmount
+ )
+ public
+ canManageStreams
+ returns (uint256 streamIndex)
+ {
+ streamIndex = streamCount++;
+ streams[streamIndex] = Stream({
+ recipient: recipient,
+ length: length,
+ startTime: uint128(block.timestamp),
+ totalAmount: totalAmount,
+ amountPaidOut: 0
+ });
+ totalUnclaimedInStreams = totalUnclaimedInStreams + totalAmount;
+ require(
+ totalUnclaimedInStreams <= yam.balanceOfUnderlying(address(this)),
+ "VestingPool::payout: Total streaming is greater than pool's YAM balance"
+ );
+ emit StreamOpened(recipient, streamIndex, length, totalAmount);
+ }
+
+ /**
+ * @dev Closes the specified stream. Pays out pending amounts, clears out the stream, and emits a StreamClosed event.
+ * @param streamId The id of the stream to close.
+ */
+ function closeStream(uint256 streamId)
+ public
+ canManageStreams
+ {
+ payout(streamId);
+ streams[streamId] = Stream(
+ address(0x0000000000000000000000000000000000000000),
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ emit StreamClosed(streamId);
+ }
+
+ /**
+ * @dev Pays out pending amount in a stream
+ * @param streamId The id of the stream to payout.
+ * @return paidOut The amount paid out in underlying
+ */
+ function payout(uint256 streamId)
+ public
+ returns (uint256 paidOut)
+ {
+ uint128 currentTime = uint128(block.timestamp);
+ Stream memory stream = streams[streamId];
+ require(
+ stream.startTime <= currentTime,
+ "VestingPool::payout: Stream hasn't started yet"
+ );
+ uint256 claimableUnderlying = _claimable(stream);
+ streams[streamId].amountPaidOut = stream.amountPaidOut + claimableUnderlying;
+
+ totalUnclaimedInStreams = totalUnclaimedInStreams - claimableUnderlying;
+
+ yam.transferUnderlying(stream.recipient, claimableUnderlying);
+
+ emit Payout(streamId, stream.recipient, claimableUnderlying);
+ return claimableUnderlying;
+ }
+
+
+ /**
+ * @dev The amount that is claimable for a stream
+ * @param streamId The stream to get the claimabout amount for.
+ * @return claimableUnderlying The amount that is claimable for this stream
+ */
+ function claimable(uint256 streamId)
+ external
+ view
+ returns (uint256 claimableUnderlying)
+ {
+ Stream memory stream = streams[streamId];
+ return _claimable(stream);
+ }
+
+ function _claimable(Stream memory stream)
+ internal
+ view
+ returns (uint256 claimableUnderlying)
+ {
+ uint128 currentTime = uint128(block.timestamp);
+ uint128 elapsedTime = currentTime - stream.startTime;
+ if (currentTime >= stream.startTime + stream.length) {
+ claimableUnderlying = stream.totalAmount - stream.amountPaidOut;
+ } else {
+ claimableUnderlying = ((elapsedTime * stream.totalAmount) / stream.length) - stream.amountPaidOut;
+ }
+ }
+
+}
diff --git a/src/utils/YAMDelegate3.sol b/src/utils/YAMDelegate3.sol
new file mode 100644
index 0000000..8324ba4
--- /dev/null
+++ b/src/utils/YAMDelegate3.sol
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+/* Copyright 2020 Compound Labs, Inc.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+// Contracts
+import "./YAMLogic3.sol";
+
+
+contract YAMDelegationStorage {
+ /**
+ * @notice Implementation address for this contract
+ */
+ address public implementation;
+}
+
+abstract contract YAMDelegatorInterface is YAMDelegationStorage {
+ /**
+ * @notice Emitted when implementation is changed
+ */
+ event NewImplementation(address oldImplementation, address newImplementation);
+
+ /**
+ * @notice Called by the gov to update the implementation of the delegator
+ * @param implementation_ The address of the new implementation for delegation
+ * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
+ * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
+ */
+ function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) virtual public;
+}
+
+abstract contract YAMDelegateInterface is YAMDelegationStorage {
+ /**
+ * @notice Called by the delegator on a delegate to initialize it for duty
+ * @dev Should revert if any issues arise which make it unfit for delegation
+ * @param data The encoded bytes data for any initialization
+ */
+ function _becomeImplementation(bytes memory data) virtual public;
+
+ /**
+ * @notice Called by the delegator on a delegate to forfeit its responsibility
+ */
+ function _resignImplementation() virtual public;
+}
+
+
+contract YAMDelegate3 is YAMLogic3, YAMDelegateInterface {
+ /**
+ * @notice Construct an empty delegate
+ */
+ constructor() public {}
+
+ /**
+ * @notice Called by the delegator on a delegate to initialize it for duty
+ * @param data The encoded bytes data for any initialization
+ */
+ function _becomeImplementation(bytes memory data) override public {
+ // Shh -- currently unused
+ data;
+
+ // Shh -- we don't ever want this hook to be marked pure
+ if (false) {
+ implementation = address(0);
+ }
+
+ require(msg.sender == gov, "only the gov may call _becomeImplementation");
+ }
+
+ /**
+ * @notice Called by the delegator on a delegate to forfeit its responsibility
+ */
+ function _resignImplementation() override public {
+ // Shh -- we don't ever want this hook to be marked pure
+ if (false) {
+ implementation = address(0);
+ }
+
+ require(msg.sender == gov, "only the gov may call _resignImplementation");
+ }
+}
diff --git a/src/utils/YAMDelegator.sol b/src/utils/YAMDelegator.sol
new file mode 100644
index 0000000..08ba0d6
--- /dev/null
+++ b/src/utils/YAMDelegator.sol
@@ -0,0 +1,512 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+import "./YAMTokenInterface.sol";
+import "./YAMDelegate3.sol";
+
+contract YAMDelegator is YAMTokenInterface, YAMDelegatorInterface {
+ /**
+ * @notice Construct a new YAM
+ * @param name_ ERC-20 name of this token
+ * @param symbol_ ERC-20 symbol of this token
+ * @param decimals_ ERC-20 decimal precision of this token
+ * @param initTotalSupply_ Initial token amount
+ * @param implementation_ The address of the implementation the contract delegates to
+ * @param becomeImplementationData The encoded args for becomeImplementation
+ */
+ constructor(
+ string memory name_,
+ string memory symbol_,
+ uint8 decimals_,
+ uint256 initTotalSupply_,
+ address implementation_,
+ bytes memory becomeImplementationData
+ )
+ public
+ {
+
+
+ // Creator of the contract is gov during initialization
+ gov = msg.sender;
+
+ // First delegate gets to initialize the delegator (i.e. storage contract)
+ delegateTo(
+ implementation_,
+ abi.encodeWithSignature(
+ "initialize(string,string,uint8,address,uint256)",
+ name_,
+ symbol_,
+ decimals_,
+ msg.sender,
+ initTotalSupply_
+ )
+ );
+
+ // New implementations always get set via the settor (post-initialize)
+ _setImplementation(implementation_, false, becomeImplementationData);
+
+ }
+
+ /**
+ * @notice Called by the gov to update the implementation of the delegator
+ * @param implementation_ The address of the new implementation for delegation
+ * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
+ * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
+ */
+ function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) override public {
+ require(msg.sender == gov, "YAMDelegator::_setImplementation: Caller must be gov");
+
+ if (allowResign) {
+ delegateToImplementation(abi.encodeWithSignature("_resignImplementation()"));
+ }
+
+ address oldImplementation = implementation;
+ implementation = implementation_;
+
+ delegateToImplementation(abi.encodeWithSignature("_becomeImplementation(bytes)", becomeImplementationData));
+
+ emit NewImplementation(oldImplementation, implementation);
+ }
+
+ /**
+ * @notice Sender supplies assets into the market and receives cTokens in exchange
+ * @dev Accrues interest whether or not the operation succeeds, unless reverted
+ * @param mintAmount The amount of the underlying asset to supply
+ * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
+ */
+ function mint(address to, uint256 mintAmount)
+ override
+ external
+ returns (bool)
+ {
+ to; mintAmount; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @notice Burns YAM from msg.sender
+ * @param burnAmount The amount of YAM to burn from msg.sender
+ * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
+ */
+ function burn(uint256 burnAmount)
+ override
+ external
+ returns (bool)
+ {
+ burnAmount; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @notice Transfer `amount` tokens from `msg.sender` to `dst`
+ * @param dst The address of the destination account
+ * @param amount The number of tokens to transfer
+ * @return Whether or not the transfer succeeded
+ */
+ function transfer(address dst, uint256 amount)
+ override
+ external
+ returns (bool)
+ {
+ dst; amount; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @notice Transfer `amount` tokens from `src` to `dst`
+ * @param src The address of the source account
+ * @param dst The address of the destination account
+ * @param amount The number of tokens to transfer
+ * @return Whether or not the transfer succeeded
+ */
+ function transferFrom(
+ address src,
+ address dst,
+ uint256 amount
+ )
+ override
+ external
+ returns (bool)
+ {
+ src; dst; amount; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @notice Approve `spender` to transfer up to `amount` from `src`
+ * @dev This will overwrite the approval amount for `spender`
+ * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
+ * @param spender The address of the account which may transfer tokens
+ * @param amount The number of tokens that are approved (-1 means infinite)
+ * @return Whether or not the approval succeeded
+ */
+ function approve(
+ address spender,
+ uint256 amount
+ )
+ override
+ external
+ returns (bool)
+ {
+ spender; amount; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @dev Increase the amount of tokens that an owner has allowed to a spender.
+ * This method should be used instead of approve() to avoid the double approval vulnerability
+ * described above.
+ * @param spender The address which will spend the funds.
+ * @param addedValue The amount of tokens to increase the allowance by.
+ */
+ function increaseAllowance(
+ address spender,
+ uint256 addedValue
+ )
+ override
+ external
+ returns (bool)
+ {
+ spender; addedValue; // Shh
+ delegateAndReturn();
+ }
+
+
+
+ function maxScalingFactor()
+ override
+ external
+ view
+ returns (uint256)
+ {
+ delegateToViewAndReturn();
+ }
+
+ function rebase(
+ uint256 epoch,
+ uint256 indexDelta,
+ bool positive
+ )
+ override
+ external
+ returns (uint256)
+ {
+ epoch; indexDelta; positive;
+ delegateAndReturn();
+ }
+
+ /**
+ * @dev Decrease the amount of tokens that an owner has allowed to a spender.
+ *
+ * @param spender The address which will spend the funds.
+ * @param subtractedValue The amount of tokens to decrease the allowance by.
+ */
+ function decreaseAllowance(
+ address spender,
+ uint256 subtractedValue
+ )
+ override
+ external
+ returns (bool)
+ {
+ spender; subtractedValue; // Shh
+ delegateAndReturn();
+ }
+
+
+ // --- Approve by signature ---
+ function permit(
+ address owner,
+ address spender,
+ uint256 value,
+ uint256 deadline,
+ uint8 v,
+ bytes32 r,
+ bytes32 s
+ )
+ external
+ {
+ owner; spender; value; deadline; v; r; s; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @notice Get the current allowance from `owner` for `spender`
+ * @param owner The address of the account which owns the tokens to be spent
+ * @param spender The address of the account which may transfer tokens
+ * @return The number of tokens allowed to be spent (-1 means infinite)
+ */
+ function allowance(
+ address owner,
+ address spender
+ )
+ override
+ external
+ view
+ returns (uint256)
+ {
+ owner; spender; // Shh
+ delegateToViewAndReturn();
+ }
+
+
+ /**
+ * @notice Rescues tokens and sends them to the `to` address
+ * @param token The address of the token
+ * @param to The address for which the tokens should be send
+ * @return Success
+ */
+ function rescueTokens(
+ address token,
+ address to,
+ uint256 amount
+ )
+ external
+ returns (bool)
+ {
+ token; to; amount; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @notice Get the current allowance from `owner` for `spender`
+ * @param delegator The address of the account which has designated a delegate
+ * @return Address of delegatee
+ */
+ function delegates(
+ address delegator
+ )
+ override
+ external
+ view
+ returns (address)
+ {
+ delegator; // Shh
+ delegateToViewAndReturn();
+ }
+
+ /**
+ * @notice Get the token balance of the `owner`
+ * @param owner The address of the account to query
+ * @return The number of tokens owned by `owner`
+ */
+ function balanceOf(address owner)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ owner; // Shh
+ delegateToViewAndReturn();
+ }
+
+ /**
+ * @notice Currently unused. For future compatability
+ * @param owner The address of the account to query
+ * @return The number of underlying tokens owned by `owner`
+ */
+ function balanceOfUnderlying(address owner)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ owner; // Shh
+ delegateToViewAndReturn();
+ }
+
+ /*** Gov Functions ***/
+
+ /**
+ * @notice Begins transfer of gov rights. The newPendingGov must call `_acceptGov` to finalize the transfer.
+ * @dev Gov function to begin change of gov. The newPendingGov must call `_acceptGov` to finalize the transfer.
+ * @param newPendingGov New pending gov.
+ */
+ function _setPendingGov(address newPendingGov)
+ override
+ external
+ {
+ newPendingGov; // Shh
+ delegateAndReturn();
+ }
+
+ function _setRebaser(address rebaser_)
+ override
+ external
+ {
+ rebaser_; // Shh
+ delegateAndReturn();
+ }
+
+ function _setIncentivizer(address incentivizer_)
+ override
+ external
+ {
+ incentivizer_; // Shh
+ delegateAndReturn();
+ }
+
+ function _setMigrator(address migrator_)
+ external
+ {
+ migrator_; // Shh
+ delegateAndReturn();
+ }
+
+ /**
+ * @notice Accepts transfer of gov rights. msg.sender must be pendingGov
+ * @dev Gov function for pending gov to accept role and update gov
+ */
+ function _acceptGov()
+ override
+ external
+ {
+ delegateAndReturn();
+ }
+
+
+ function getPriorVotes(address account, uint blockNumber)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ account; blockNumber;
+ delegateToViewAndReturn();
+ }
+
+ function delegateBySig(
+ address delegatee,
+ uint nonce,
+ uint expiry,
+ uint8 v,
+ bytes32 r,
+ bytes32 s
+ )
+ override
+ external
+ {
+ delegatee; nonce; expiry; v; r; s;
+ delegateAndReturn();
+ }
+
+ function delegate(address delegatee)
+ override
+ external
+ {
+ delegatee;
+ delegateAndReturn();
+ }
+
+ function getCurrentVotes(address account)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ account;
+ delegateToViewAndReturn();
+ }
+
+
+ function yamToFragment(uint256 yam)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ yam;
+ delegateToViewAndReturn();
+ }
+
+ function fragmentToYam(uint256 value)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ value;
+ delegateToViewAndReturn();
+ }
+
+ /**
+ * @notice Internal method to delegate execution to another contract
+ * @dev It returns to the external caller whatever the implementation returns or forwards reverts
+ * @param callee The contract to delegatecall
+ * @param data The raw data to delegatecall
+ * @return The returned bytes from the delegatecall
+ */
+ function delegateTo(address callee, bytes memory data) internal returns (bytes memory) {
+ (bool success, bytes memory returnData) = callee.delegatecall(data);
+ assembly {
+ if eq(success, 0) {
+ revert(add(returnData, 0x20), returndatasize())
+ }
+ }
+ return returnData;
+ }
+
+ /**
+ * @notice Delegates execution to the implementation contract
+ * @dev It returns to the external caller whatever the implementation returns or forwards reverts
+ * @param data The raw data to delegatecall
+ * @return The returned bytes from the delegatecall
+ */
+ function delegateToImplementation(bytes memory data) public returns (bytes memory) {
+ return delegateTo(implementation, data);
+ }
+
+ /**
+ * @notice Delegates execution to an implementation contract
+ * @dev It returns to the external caller whatever the implementation returns or forwards reverts
+ * There are an additional 2 prefix uints from the wrapper returndata, which we ignore since we make an extra hop.
+ * @param data The raw data to delegatecall
+ * @return The returned bytes from the delegatecall
+ */
+ function delegateToViewImplementation(bytes memory data) public view returns (bytes memory) {
+ (bool success, bytes memory returnData) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", data));
+ assembly {
+ if eq(success, 0) {
+ revert(add(returnData, 0x20), returndatasize())
+ }
+ }
+ return abi.decode(returnData, (bytes));
+ }
+
+ function delegateToViewAndReturn() private view returns (bytes memory) {
+ (bool success, ) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", msg.data));
+
+ assembly {
+ let free_mem_ptr := mload(0x40)
+ returndatacopy(free_mem_ptr, 0, returndatasize())
+
+ switch success
+ case 0 { revert(free_mem_ptr, returndatasize()) }
+ default { return(add(free_mem_ptr, 0x40), sub(returndatasize(), 0x40)) }
+ }
+ }
+
+ function delegateAndReturn() private returns (bytes memory) {
+ (bool success, ) = implementation.delegatecall(msg.data);
+
+ assembly {
+ let free_mem_ptr := mload(0x40)
+ returndatacopy(free_mem_ptr, 0, returndatasize())
+
+ switch success
+ case 0 { revert(free_mem_ptr, returndatasize()) }
+ default { return(free_mem_ptr, returndatasize()) }
+ }
+ }
+
+ /**
+ * @notice Delegates execution to an implementation contract
+ * @dev It returns to the external caller whatever the implementation returns or forwards reverts
+ */
+ fallback() external {
+ // require(msg.value == 0,"YAMDelegator:fallback: cannot send value to fallback");
+
+ // delegate all other functions to current implementation
+ delegateAndReturn();
+ }
+}
diff --git a/src/utils/YAMGovernanceStorage.sol b/src/utils/YAMGovernanceStorage.sol
new file mode 100644
index 0000000..5d888ae
--- /dev/null
+++ b/src/utils/YAMGovernanceStorage.sol
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+/* Copyright 2020 Compound Labs, Inc.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+
+contract YAMGovernanceStorage {
+ /// @notice A record of each accounts delegate
+ mapping(address => address) internal _delegates;
+
+ /// @notice A checkpoint for marking number of votes from a given block
+ struct Checkpoint {
+ uint32 fromBlock;
+ uint256 votes;
+ }
+
+ /// @notice A record of votes checkpoints for each account, by index
+ mapping (address => mapping(uint32 => Checkpoint)) public checkpoints;
+
+ /// @notice The number of checkpoints for each account
+ mapping(address => uint32) public numCheckpoints;
+
+ /// @notice The EIP-712 typehash for the contract's domain
+ bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
+
+ /// @notice The EIP-712 typehash for the delegation struct used by the contract
+ bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
+
+ /// @notice A record of states for signing / validating signatures
+ mapping(address => uint) public nonces;
+}
diff --git a/src/utils/YAMGovernanceToken.sol b/src/utils/YAMGovernanceToken.sol
new file mode 100644
index 0000000..3ad2ec5
--- /dev/null
+++ b/src/utils/YAMGovernanceToken.sol
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+/* Copyright 2020 Compound Labs, Inc.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+
+// Contracts
+import "./YAMGovernanceStorage.sol";
+import "./YAMTokenInterface.sol";
+
+
+abstract contract YAMGovernanceToken is YAMTokenInterface {
+ /**
+ * @notice Get delegatee for an address delegating
+ * @param delegator The address to get delegatee for
+ */
+ function delegates(address delegator)
+ override
+ external
+ view
+ returns (address)
+ {
+ return _delegates[delegator];
+ }
+
+ /**
+ * @notice Delegate votes from `msg.sender` to `delegatee`
+ * @param delegatee The address to delegate votes to
+ */
+ function delegate(address delegatee) override external {
+ return _delegate(msg.sender, delegatee);
+ }
+
+ /**
+ * @notice Delegates votes from signatory to `delegatee`
+ * @param delegatee The address to delegate votes to
+ * @param nonce The contract state required to match the signature
+ * @param expiry The time at which to expire the signature
+ * @param v The recovery byte of the signature
+ * @param r Half of the ECDSA signature pair
+ * @param s Half of the ECDSA signature pair
+ */
+ function delegateBySig(
+ address delegatee,
+ uint nonce,
+ uint expiry,
+ uint8 v,
+ bytes32 r,
+ bytes32 s
+ )
+ override
+ external
+ {
+ bytes32 structHash = keccak256(
+ abi.encode(
+ DELEGATION_TYPEHASH,
+ delegatee,
+ nonce,
+ expiry
+ )
+ );
+
+ bytes32 digest = keccak256(
+ abi.encodePacked(
+ "\x19\x01",
+ DOMAIN_SEPARATOR,
+ structHash
+ )
+ );
+
+ address signatory = ecrecover(digest, v, r, s);
+ require(signatory != address(0), "YAM::delegateBySig: invalid signature");
+ require(nonce == nonces[signatory]++, "YAM::delegateBySig: invalid nonce");
+ require(block.timestamp <= expiry, "YAM::delegateBySig: signature expired");
+ return _delegate(signatory, delegatee);
+ }
+
+ /**
+ * @notice Gets the current votes balance for `account`
+ * @param account The address to get votes balance
+ * @return The number of current votes for `account`
+ */
+ function getCurrentVotes(address account)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ uint32 nCheckpoints = numCheckpoints[account];
+ return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
+ }
+
+ /**
+ * @notice Determine the prior number of votes for an account as of a block number
+ * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
+ * @param account The address of the account to check
+ * @param blockNumber The block number to get the vote balance at
+ * @return The number of votes the account had as of the given block
+ */
+ function getPriorVotes(address account, uint blockNumber)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ require(blockNumber < block.number, "YAM::getPriorVotes: not yet determined");
+
+ uint32 nCheckpoints = numCheckpoints[account];
+ if (nCheckpoints == 0) {
+ return 0;
+ }
+
+ // First check most recent balance
+ if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
+ return checkpoints[account][nCheckpoints - 1].votes;
+ }
+
+ // Next check implicit zero balance
+ if (checkpoints[account][0].fromBlock > blockNumber) {
+ return 0;
+ }
+
+ uint32 lower = 0;
+ uint32 upper = nCheckpoints - 1;
+ while (upper > lower) {
+ uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
+ Checkpoint memory cp = checkpoints[account][center];
+ if (cp.fromBlock == blockNumber) {
+ return cp.votes;
+ } else if (cp.fromBlock < blockNumber) {
+ lower = center;
+ } else {
+ upper = center - 1;
+ }
+ }
+ return checkpoints[account][lower].votes;
+ }
+
+ function _delegate(address delegator, address delegatee)
+ internal
+ {
+ address currentDelegate = _delegates[delegator];
+ uint256 delegatorBalance = _yamBalances[delegator]; // balance of underlying YAMs (not scaled);
+ _delegates[delegator] = delegatee;
+
+ emit DelegateChanged(delegator, currentDelegate, delegatee);
+
+ _moveDelegates(currentDelegate, delegatee, delegatorBalance);
+ }
+
+ function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
+ if (srcRep != dstRep && amount > 0) {
+ if (srcRep != address(0)) {
+ // decrease old representative
+ uint32 srcRepNum = numCheckpoints[srcRep];
+ uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
+ uint256 srcRepNew = srcRepOld - amount;
+ _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
+ }
+
+ if (dstRep != address(0)) {
+ // increase new representative
+ uint32 dstRepNum = numCheckpoints[dstRep];
+ uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
+ uint256 dstRepNew = dstRepOld + amount;
+ _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
+ }
+ }
+ }
+
+ function _writeCheckpoint(
+ address delegatee,
+ uint32 nCheckpoints,
+ uint256 oldVotes,
+ uint256 newVotes
+ )
+ internal
+ {
+ uint32 blockNumber = safe32(block.number, "YAM::_writeCheckpoint: block number exceeds 32 bits");
+
+ if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
+ checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
+ } else {
+ checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
+ numCheckpoints[delegatee] = nCheckpoints + 1;
+ }
+
+ emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
+ }
+
+ function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
+ require(n < 2**32, errorMessage);
+ return uint32(n);
+ }
+
+ function getChainId() internal view returns (uint) {
+ uint256 chainId;
+ assembly { chainId := chainid() }
+ return chainId;
+ }
+}
diff --git a/src/utils/YAMHelper.sol b/src/utils/YAMHelper.sol
new file mode 100644
index 0000000..eac4b11
--- /dev/null
+++ b/src/utils/YAMHelper.sol
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+import {YAMDelegator} from "./YAMDelegator.sol";
+import {DSTest} from "ds-test/test.sol";
+import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
+
+interface Hevm {
+ function warp(uint) external;
+ function roll(uint) external;
+ function store(address,bytes32,bytes32) external;
+ function load(address,bytes32) external returns (bytes32);
+}
+
+contract HEVMHelpers is DSTest {
+
+ event Debug(uint, bytes32);
+ event SlotFound(address who, string sig, uint slot);
+ event Logger(uint, bytes);
+
+ Hevm hevm = Hevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
+
+ mapping (address => mapping(bytes4 => uint256)) public slots;
+ mapping (address => mapping(bytes4 => bool)) public finds;
+
+ function sigs(
+ string memory sig
+ )
+ public
+ pure
+ returns (bytes4)
+ {
+ return bytes4(keccak256(bytes(sig)));
+ }
+
+ /// @notice find an arbitrary storage slot given a function sig, input data, address of the contract and a value to check against
+ // slot complexity:
+ // if flat, will be bytes32(uint256(uint));
+ // if map, will be keccak256(abi.encode(key, uint(slot)));
+ // if deep map, will be keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))));
+ // if map struct, will be bytes32(uint256(keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))))) + structFieldDepth);
+ function find(
+ string memory sig, // signature to check agains
+ bytes32[] memory ins, // see slot complexity
+ address who, // contract
+ bytes32 set
+ ) public {
+ // calldata to test against
+ bytes4 fsig = bytes4(keccak256(bytes(sig)));
+ bytes memory dat = flatten(ins);
+ bytes memory cald = abi.encodePacked(fsig, dat);
+
+ // iterate thru slots
+ for (uint256 i = 0; i < 30; i++) {
+ bytes32 slot;
+ if (ins.length > 0) {
+ for (uint256 j = 0; j < ins.length; j++) {
+ if (j != 0) {
+ slot = keccak256(abi.encode(ins[j], slot));
+ } else {
+ slot = keccak256(abi.encode(ins[j], uint(i)));
+ }
+ }
+ } else {
+ // no ins, so should be flat
+ slot = bytes32(i);
+ }
+ // load slot
+ bytes32 prev = hevm.load(who, slot);
+ // store
+ hevm.store(who, slot, set);
+ // call
+ (bool pass, bytes memory rdat) = who.staticcall(cald);
+ pass; // ssh
+ bytes32 fdat = bytesToBytes32(rdat, 0);
+ // check if good
+ if (fdat == set) {
+ slots[who][fsig] = i;
+ finds[who][fsig] = true;
+ hevm.store(who, slot, prev);
+ emit SlotFound(who, sig, i);
+ break;
+ }
+ // reset storage
+ hevm.store(who, slot, prev);
+ }
+
+ require(finds[who][fsig], "!found");
+ }
+
+ /// @notice write to an arbitrary slot given a function signature
+ function writ(
+ string memory sig, // signature to check agains
+ bytes32[] memory ins, // see slot complexity
+ uint256 depth, // see slot complexity
+ address who, // contract
+ bytes32 set // value to set storage as
+ ) public {
+ bytes4 fsig = sigs(sig);
+
+ require(finds[who][fsig], "!found");
+ bytes32 slot;
+ if (ins.length > 0) {
+ for (uint256 j = 0; j < ins.length; j++) {
+ if (j != 0) {
+ slot = keccak256(abi.encode(ins[j], slot));
+ } else {
+ slot = keccak256(abi.encode(ins[j], slots[who][fsig]));
+ }
+ }
+ } else {
+ // no ins, so should be flat
+ slot = bytes32(slots[who][fsig]);
+ }
+ // add depth -- noop if 0
+ slot = bytes32(uint256(slot) + depth);
+ // set storage
+ hevm.store(who, slot, set);
+ }
+
+ function write_flat(address who, string memory sig, uint256 value) public {
+ bytes32[] memory ins = new bytes32[](0);
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ ins,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ ins,
+ 0,
+ who,
+ bytes32(value)
+ );
+ }
+
+ function write_flat(address who, string memory sig, address value) public {
+ bytes32[] memory ins = new bytes32[](0);
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ ins,
+ who,
+ bytes32(uint256(uint160(0xaaaCfBec6a24756c20D41914f2CABA817C0d8521)))
+ );
+ }
+ writ(
+ sig,
+ ins,
+ 0,
+ who,
+ bytes32(uint256(uint160(value)))
+ );
+ }
+
+ function write_map(address who, string memory sig, uint256 key, uint256 value) public {
+ bytes32[] memory keys = new bytes32[](1);
+ keys[0] = bytes32(uint256(key));
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ 0,
+ who,
+ bytes32(value)
+ );
+ }
+
+ function write_map(address who, string memory sig, uint256 key, address value) public {
+ bytes32[] memory keys = new bytes32[](1);
+ keys[0] = bytes32(uint256(key));
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ 0,
+ who,
+ bytes32(uint256(uint160(value)))
+ );
+ }
+
+
+ function write_map(address who, string memory sig, address key, uint256 value) public {
+ bytes32[] memory keys = new bytes32[](1);
+ keys[0] = bytes32(uint256(uint160(key)));
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ 0,
+ who,
+ bytes32(value)
+ );
+ }
+
+ function write_map(address who, string memory sig, address key, address value) public {
+ bytes32[] memory keys = new bytes32[](1);
+ keys[0] = bytes32(uint256(uint160(key)));
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ 0,
+ who,
+ bytes32(uint256(uint160(value)))
+ );
+ }
+
+ function write_deep_map(address who, string memory sig, bytes32[] memory keys, uint256 value) public {
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ 0,
+ who,
+ bytes32(value)
+ );
+ }
+
+ function write_deep_map(address who, string memory sig, bytes32[] memory keys, address value) public {
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ 0,
+ who,
+ bytes32(uint256(uint160(value)))
+ );
+ }
+
+ function write_deep_map_struct(address who, string memory sig, bytes32[] memory keys, uint256 value, uint256 depth) public {
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ depth,
+ who,
+ bytes32(value)
+ );
+ }
+
+ function write_deep_map_struct(address who, string memory sig, bytes32[] memory keys, address value, uint256 depth) public {
+ if (!finds[who][sigs(sig)]) {
+ find(
+ sig,
+ keys,
+ who,
+ bytes32(uint256(13371337))
+ );
+ }
+ writ(
+ sig,
+ keys,
+ depth,
+ who,
+ bytes32(uint256(uint160(value)))
+ );
+ }
+
+ function bytesToBytes32(bytes memory b, uint offset) public pure returns (bytes32) {
+ bytes32 out;
+
+ for (uint i = 0; i < 32; i++) {
+ out |= bytes32(b[offset + i] & 0xFF) >> (i * 8);
+ }
+ return out;
+ }
+
+ function flatten(bytes32[] memory b) public pure returns (bytes memory)
+ {
+ bytes memory result = new bytes(b.length * 32);
+ for (uint256 i = 0; i < b.length; i++) {
+ bytes32 k = b[i];
+ assembly {
+ mstore(add(result, add(32, mul(32, i))), k)
+ }
+ }
+
+ return result;
+ }
+
+ function addKnownHEVM(address who, bytes4 fsig, uint slot) public {
+ slots[who][fsig] = slot;
+ finds[who][fsig] = true;
+ }
+
+}
+
+interface YAM {
+ function fragmentToYam(uint256) external returns (uint256);
+ function yamToFragment(uint256) external returns (uint256);
+}
+
+contract YAMHelper is HEVMHelpers {
+
+ address yamAddr = address(0x0AaCfbeC6a24756c20D41914F2caba817C0d8521);
+
+ function addKnown(address yam, string memory sig, uint256 slot) public {
+ addKnownHEVM(yam, sigs(sig), slot);
+ }
+
+ function write_balanceOf(address who, address acct, uint256 value) public {
+ if (who == yamAddr) {
+ writeBoU(YAMDelegator(address(uint160(yamAddr))), acct, YAMDelegator(address(uint160(yamAddr))).fragmentToYam(value));
+ } else {
+ uint256 bal = IERC20(who).balanceOf(acct);
+ write_map(who, "balanceOf(address)", acct, value);
+
+ uint256 newTS;
+ if (bal > value) {
+ uint256 negdelta = bal - value;
+ newTS = IERC20(who).totalSupply() - negdelta;
+ } else {
+ uint256 posdelta = value - bal;
+ newTS = IERC20(who).totalSupply() + posdelta;
+ }
+
+ write_flat(who, "totalSupply()", newTS);
+ // assertEq(IERC20(who).totalSupply(), newTS);
+ }
+ }
+
+ function write_balanceOfUnderlying(address who, address acct, uint256 value) public {
+ write_map(who, "balanceOfUnderlying(address)", acct, value);
+ }
+
+ function writeBoU(YAMDelegator yamV3, address account, uint256 value) public {
+ uint256 bal = yamV3.balanceOfUnderlying(account);
+ write_map(address(yamV3), "balanceOfUnderlying(address)", account, value);
+ // assertEq(yamV3.balanceOfUnderlying(account), value);
+ write_last_checkpoint(yamV3, account, value);
+
+ uint256 newIS;
+ uint256 newTS;
+ if (bal > value) {
+ uint256 negdelta = bal - value;
+ newIS = yamV3.initSupply() - negdelta;
+ newTS = yamV3.yamToFragment(newIS);
+ } else {
+ uint256 posdelta = value - bal;
+ newIS = yamV3.initSupply() + posdelta;
+ newTS = yamV3.yamToFragment(newIS);
+ }
+
+ write_flat(address(yamV3), "initSupply()", newIS);
+ // assertEq(yamV3.initSupply(), newIS);
+ write_flat(address(yamV3), "totalSupply()", newTS);
+ // assertEq(yamV3.totalSupply(), newTS);
+ }
+
+ function getProposal(YAMDelegator yamV3, address account) public {
+ writeBoU(yamV3, account, 10**24*51000);
+ }
+
+ function getQuorum(YAMDelegator yamV3, address account) public {
+ writeBoU(yamV3, account, 10**24*210000);
+ bing();
+ }
+
+ function becomeGovernor(address who, address account) public {
+ write_flat(who, "pendingGov()", account);
+ }
+
+ // may or may not work depending on storage layout
+ function becomeGovernorDirect(address who, address account) public {
+ write_flat(who, "gov()", account);
+ }
+
+ function becomeAdmin(address who, address account) public {
+ write_flat(who, "admin()", account);
+ }
+
+ function manualCheckpoint(YAMDelegator yamV3, address account, uint256 checkpoint, uint256 fromBlock, uint256 votes) public {
+ bytes32[] memory keys = new bytes32[](2);
+ keys[0] = bytes32(uint256(uint160(account)));
+ keys[1] = bytes32(uint256(safe32(checkpoint, "")));
+ write_deep_map_struct(address(yamV3), "checkpoints(address,uint32)", keys, fromBlock, 0);
+ write_deep_map_struct(address(yamV3), "checkpoints(address,uint32)", keys, votes, 1);
+ }
+
+ function write_last_checkpoint(YAMDelegator yamV3, address account, uint256 votes) public {
+ uint256 lcp = yamV3.numCheckpoints(account);
+ if (lcp > 0) {
+ lcp = lcp - 1;
+ }
+ bytes32[] memory keys = new bytes32[](2);
+ keys[0] = bytes32(uint256(uint160(account)));
+ keys[1] = bytes32(uint256(safe32(lcp, "")));
+ write_deep_map_struct(address(yamV3), "checkpoints(address,uint32)", keys, votes, 1);
+ if (lcp == 0) {
+ write_deep_map_struct(address(yamV3), "checkpoints(address,uint32)", keys, block.number - 1, 0);
+ write_map(address(yamV3), "numCheckpoints(address)", account, 1);
+ }
+ (uint32 fromBlock_post, uint256 votes_post ) = yamV3.checkpoints(account, safe32(lcp, ""));
+ // assertEq(uint256(fromBlock_post), block.number - 1);
+ // assertEq(votes_post, votes);
+ }
+
+ function safe32(uint n, string memory errorMessage) public pure returns (uint32) {
+ require(n < 2**32, errorMessage);
+ return uint32(n);
+ }
+
+ function bing() public {
+ hevm.roll(block.number + 1);
+ }
+
+ function bong(uint256 x) public {
+ hevm.roll(block.number + x);
+ }
+
+ function ff(uint256 time) public {
+ hevm.warp(block.timestamp + time);
+ }
+}
diff --git a/src/utils/YAMLogic3.sol b/src/utils/YAMLogic3.sol
new file mode 100644
index 0000000..16d4a70
--- /dev/null
+++ b/src/utils/YAMLogic3.sol
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+// Interfaces
+import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
+
+// Libraries
+import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
+
+// Contracts
+import {YAMGovernanceToken} from "./YAMGovernanceToken.sol";
+
+
+contract YAMToken is YAMGovernanceToken {
+ // Modifiers
+ modifier onlyGov() {
+ require(msg.sender == gov);
+ _;
+ }
+
+ modifier onlyRebaser() {
+ require(msg.sender == rebaser);
+ _;
+ }
+
+ modifier onlyMinter() {
+ require(
+ msg.sender == rebaser
+ || msg.sender == gov
+ || msg.sender == incentivizer
+ || msg.sender == migrator,
+ "not minter"
+ );
+ _;
+ }
+
+ modifier validRecipient(address to) {
+ require(to != address(0x0));
+ require(to != address(this));
+ _;
+ }
+
+ function initialize(
+ string memory name_,
+ string memory symbol_,
+ uint8 decimals_
+ )
+ public
+ {
+ require(yamsScalingFactor == 0, "already initialized");
+ name = name_;
+ symbol = symbol_;
+ decimals = decimals_;
+ }
+
+
+ /**
+ * @notice Computes the current max scaling factor
+ */
+ function maxScalingFactor()
+ override
+ external
+ view
+ returns (uint256)
+ {
+ return _maxScalingFactor();
+ }
+
+ function _maxScalingFactor()
+ internal
+ view
+ returns (uint256)
+ {
+ // scaling factor can only go up to 2**256-1 = initSupply * yamsScalingFactor
+ // this is used to check if yamsScalingFactor will be too high to compute balances when rebasing.
+ return type(uint256).max / initSupply;
+ }
+
+ /**
+ * @notice Mints new tokens, increasing totalSupply, initSupply, and a users balance.
+ * @dev Limited to onlyMinter modifier
+ */
+ function mint(address to, uint256 amount)
+ override
+ external
+ onlyMinter
+ returns (bool)
+ {
+ _mint(to, amount);
+ return true;
+ }
+
+ function _mint(address to, uint256 amount)
+ internal
+ {
+ if (msg.sender == migrator) {
+ // migrator directly uses v2 balance for the amount
+
+ // increase initSupply
+ initSupply = initSupply + amount;
+
+ // get external value
+ uint256 scaledAmount = _yamToFragment(amount);
+
+ // increase totalSupply
+ totalSupply = totalSupply + scaledAmount;
+
+ // make sure the mint didnt push maxScalingFactor too low
+ require(yamsScalingFactor <= _maxScalingFactor(), "max scaling factor too low");
+
+ // add balance
+ _yamBalances[to] = _yamBalances[to] + amount;
+
+ // add delegates to the minter
+ _moveDelegates(address(0), _delegates[to], amount);
+ emit Mint(to, scaledAmount);
+ emit Transfer(address(0), to, scaledAmount);
+ } else {
+ // increase totalSupply
+ totalSupply = totalSupply + amount;
+
+ // get underlying value
+ uint256 yamValue = _fragmentToYam(amount);
+
+ // increase initSupply
+ initSupply = initSupply + yamValue;
+
+ // make sure the mint didnt push maxScalingFactor too low
+ require(yamsScalingFactor <= _maxScalingFactor(), "max scaling factor too low");
+
+ // add balance
+ _yamBalances[to] = _yamBalances[to] + yamValue;
+
+ // add delegates to the minter
+ _moveDelegates(address(0), _delegates[to], yamValue);
+ emit Mint(to, amount);
+ emit Transfer(address(0), to, amount);
+ }
+ }
+
+ /**
+ * @notice Burns tokens from msg.sender, decreases totalSupply, initSupply, and a users balance.
+ */
+
+ function burn(uint256 amount)
+ override
+ external
+ returns (bool)
+ {
+ _burn(amount);
+ return true;
+ }
+
+ function _burn(uint256 amount)
+ internal
+ {
+ // decrease totalSupply
+ totalSupply = totalSupply - amount;
+
+ // get underlying value
+ uint256 yamValue = _fragmentToYam(amount);
+
+ // decrease initSupply
+ initSupply = initSupply - yamValue;
+
+ // decrease balance
+ _yamBalances[msg.sender] = _yamBalances[msg.sender] - yamValue;
+
+ // add delegates to the minter
+ _moveDelegates(_delegates[msg.sender], address(0), yamValue);
+ emit Burn(msg.sender, amount);
+ emit Transfer(msg.sender, address(0), amount);
+ }
+
+ /**
+ * @notice Mints new tokens using underlying amount, increasing totalSupply, initSupply, and a users balance.
+ * @dev Limited to onlyMinter modifier
+ */
+ function mintUnderlying(address to, uint256 amount)
+ external
+ onlyMinter
+ returns (bool)
+ {
+ _mintUnderlying(to, amount);
+ return true;
+ }
+
+ function _mintUnderlying(address to, uint256 amount)
+ internal
+ {
+
+ // increase initSupply
+ initSupply = initSupply + amount;
+
+ // get external value
+ uint256 scaledAmount = _yamToFragment(amount);
+
+ // increase totalSupply
+ totalSupply = totalSupply + scaledAmount;
+
+ // make sure the mint didnt push maxScalingFactor too low
+ require(yamsScalingFactor <= _maxScalingFactor(), "max scaling factor too low");
+
+ // add balance
+ _yamBalances[to] = _yamBalances[to] + amount;
+
+ // add delegates to the minter
+ _moveDelegates(address(0), _delegates[to], amount);
+ emit Mint(to, scaledAmount);
+ emit Transfer(address(0), to, scaledAmount);
+
+ }
+
+ /**
+ * @dev Transfer underlying balance to a specified address.
+ * @param to The address to transfer to.
+ * @param value The amount to be transferred.
+ * @return True on success, false otherwise.
+ */
+ function transferUnderlying(address to, uint256 value)
+ external
+ validRecipient(to)
+ returns (bool)
+ {
+ // sub from balance of sender
+ _yamBalances[msg.sender] = _yamBalances[msg.sender] - value;
+
+ // add to balance of receiver
+ _yamBalances[to] = _yamBalances[to] + value;
+ emit Transfer(msg.sender, to, _yamToFragment(value));
+
+ _moveDelegates(_delegates[msg.sender], _delegates[to], value);
+ return true;
+ }
+
+ /* - ERC20 functionality - */
+
+ /**
+ * @dev Transfer tokens to a specified address.
+ * @param to The address to transfer to.
+ * @param value The amount to be transferred.
+ * @return True on success, false otherwise.
+ */
+ function transfer(address to, uint256 value)
+ override
+ external
+ validRecipient(to)
+ returns (bool)
+ {
+ // underlying balance is stored in yams, so divide by current scaling factor
+
+ // note, this means as scaling factor grows, dust will be untransferrable.
+ // minimum transfer value == yamsScalingFactor / 1e24;
+
+ // get amount in underlying
+ uint256 yamValue = _fragmentToYam(value);
+
+ // sub from balance of sender
+ _yamBalances[msg.sender] = _yamBalances[msg.sender] - yamValue;
+
+ // add to balance of receiver
+ _yamBalances[to] = _yamBalances[to] + yamValue;
+ emit Transfer(msg.sender, to, value);
+
+ _moveDelegates(_delegates[msg.sender], _delegates[to], yamValue);
+ return true;
+ }
+
+ /**
+ * @dev Transfer tokens from one address to another.
+ * @param from The address you want to send tokens from.
+ * @param to The address you want to transfer to.
+ * @param value The amount of tokens to be transferred.
+ */
+ function transferFrom(address from, address to, uint256 value)
+ override
+ external
+ validRecipient(to)
+ returns (bool)
+ {
+ // decrease allowance
+ _allowedFragments[from][msg.sender] = _allowedFragments[from][msg.sender] - value;
+
+ // get value in yams
+ uint256 yamValue = _fragmentToYam(value);
+
+ // sub from from
+ _yamBalances[from] = _yamBalances[from] - yamValue;
+ _yamBalances[to] = _yamBalances[to] + yamValue;
+ emit Transfer(from, to, value);
+
+ _moveDelegates(_delegates[from], _delegates[to], yamValue);
+ return true;
+ }
+
+ /**
+ * @param who The address to query.
+ * @return The balance of the specified address.
+ */
+ function balanceOf(address who)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ return _yamToFragment(_yamBalances[who]);
+ }
+
+ /** @notice Currently returns the internal storage amount
+ * @param who The address to query.
+ * @return The underlying balance of the specified address.
+ */
+ function balanceOfUnderlying(address who)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ return _yamBalances[who];
+ }
+
+ /**
+ * @dev Function to check the amount of tokens that an owner has allowed to a spender.
+ * @param owner_ The address which owns the funds.
+ * @param spender The address which will spend the funds.
+ * @return The number of tokens still available for the spender.
+ */
+ function allowance(address owner_, address spender)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ return _allowedFragments[owner_][spender];
+ }
+
+ /**
+ * @dev Approve the passed address to spend the specified amount of tokens on behalf of
+ * msg.sender. This method is included for ERC20 compatibility.
+ * increaseAllowance and decreaseAllowance should be used instead.
+ * Changing an allowance with this method brings the risk that someone may transfer both
+ * the old and the new allowance - if they are both greater than zero - if a transfer
+ * transaction is mined before the later approve() call is mined.
+ *
+ * @param spender The address which will spend the funds.
+ * @param value The amount of tokens to be spent.
+ */
+ function approve(address spender, uint256 value)
+ override
+ external
+ returns (bool)
+ {
+ _allowedFragments[msg.sender][spender] = value;
+ emit Approval(msg.sender, spender, value);
+ return true;
+ }
+
+ /**
+ * @dev Increase the amount of tokens that an owner has allowed to a spender.
+ * This method should be used instead of approve() to avoid the double approval vulnerability
+ * described above.
+ * @param spender The address which will spend the funds.
+ * @param addedValue The amount of tokens to increase the allowance by.
+ */
+ function increaseAllowance(address spender, uint256 addedValue)
+ override
+ external
+ returns (bool)
+ {
+ _allowedFragments[msg.sender][spender] =
+ _allowedFragments[msg.sender][spender] + addedValue;
+ emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
+ return true;
+ }
+
+ /**
+ * @dev Decrease the amount of tokens that an owner has allowed to a spender.
+ *
+ * @param spender The address which will spend the funds.
+ * @param subtractedValue The amount of tokens to decrease the allowance by.
+ */
+ function decreaseAllowance(address spender, uint256 subtractedValue)
+ override
+ external
+ returns (bool)
+ {
+ uint256 oldValue = _allowedFragments[msg.sender][spender];
+ if (subtractedValue >= oldValue) {
+ _allowedFragments[msg.sender][spender] = 0;
+ } else {
+ _allowedFragments[msg.sender][spender] = oldValue - subtractedValue;
+ }
+ emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
+ return true;
+ }
+
+ // --- Approve by signature ---
+ function permit(
+ address owner,
+ address spender,
+ uint256 value,
+ uint256 deadline,
+ uint8 v,
+ bytes32 r,
+ bytes32 s
+ )
+ external
+ {
+ require(block.timestamp <= deadline, "YAM/permit-expired");
+
+ bytes32 digest =
+ keccak256(
+ abi.encodePacked(
+ "\x19\x01",
+ DOMAIN_SEPARATOR,
+ keccak256(
+ abi.encode(
+ PERMIT_TYPEHASH,
+ owner,
+ spender,
+ value,
+ nonces[owner]++,
+ deadline
+ )
+ )
+ )
+ );
+
+ require(owner != address(0), "YAM/invalid-address-0");
+ require(owner == ecrecover(digest, v, r, s), "YAM/invalid-permit");
+ _allowedFragments[owner][spender] = value;
+ emit Approval(owner, spender, value);
+ }
+
+ /* - Governance Functions - */
+
+ /** @notice sets the rebaser
+ * @param rebaser_ The address of the rebaser contract to use for authentication.
+ */
+ function _setRebaser(address rebaser_)
+ override
+ external
+ onlyGov
+ {
+ address oldRebaser = rebaser;
+ rebaser = rebaser_;
+ emit NewRebaser(oldRebaser, rebaser_);
+ }
+
+ /** @notice sets the migrator
+ * @param migrator_ The address of the migrator contract to use for authentication.
+ */
+ function _setMigrator(address migrator_)
+ external
+ onlyGov
+ {
+ address oldMigrator = migrator_;
+ migrator = migrator_;
+ emit NewMigrator(oldMigrator, migrator_);
+ }
+
+ /** @notice sets the incentivizer
+ * @param incentivizer_ The address of the rebaser contract to use for authentication.
+ */
+ function _setIncentivizer(address incentivizer_)
+ override
+ external
+ onlyGov
+ {
+ address oldIncentivizer = incentivizer;
+ incentivizer = incentivizer_;
+ emit NewIncentivizer(oldIncentivizer, incentivizer_);
+ }
+
+ /** @notice sets the pendingGov
+ * @param pendingGov_ The address of the rebaser contract to use for authentication.
+ */
+ function _setPendingGov(address pendingGov_)
+ override
+ external
+ onlyGov
+ {
+ address oldPendingGov = pendingGov;
+ pendingGov = pendingGov_;
+ emit NewPendingGov(oldPendingGov, pendingGov_);
+ }
+
+ /** @notice allows governance to assign delegate to self
+ *
+ */
+ function _acceptGov()
+ override
+ external
+ {
+ require(msg.sender == pendingGov, "!pending");
+ address oldGov = gov;
+ gov = pendingGov;
+ pendingGov = address(0);
+ emit NewGov(oldGov, gov);
+ }
+
+ function assignSelfDelegate(address nonvotingContract)
+ external
+ onlyGov
+ {
+ address delegate = _delegates[nonvotingContract];
+ require( delegate == address(0), "!address(0)" );
+ // assigns delegate to self only
+ _delegate(nonvotingContract, nonvotingContract);
+ }
+
+ /* - Extras - */
+
+ /**
+ * @notice Initiates a new rebase operation, provided the minimum time period has elapsed.
+ *
+ * @dev The supply adjustment equals (totalSupply * DeviationFromTargetRate) / rebaseLag
+ * Where DeviationFromTargetRate is (MarketOracleRate - targetRate) / targetRate
+ * and targetRate is CpiOracleRate / baseCpi
+ */
+ function rebase(
+ uint256 epoch,
+ uint256 indexDelta,
+ bool positive
+ )
+ override
+ external
+ onlyRebaser
+ returns (uint256)
+ {
+ // no change
+ if (indexDelta == 0) {
+ emit Rebase(epoch, yamsScalingFactor, yamsScalingFactor);
+ return totalSupply;
+ }
+
+ // for events
+ uint256 prevYamsScalingFactor = yamsScalingFactor;
+
+
+ if (!positive) {
+ // negative rebase, decrease scaling factor
+ yamsScalingFactor = (yamsScalingFactor * (BASE - indexDelta)) / BASE;
+ } else {
+ // positive reabse, increase scaling factor
+ uint256 newScalingFactor = (yamsScalingFactor * (BASE + indexDelta)) / BASE;
+ if (newScalingFactor < _maxScalingFactor()) {
+ yamsScalingFactor = newScalingFactor;
+ } else {
+ yamsScalingFactor = _maxScalingFactor();
+ }
+ }
+
+ // update total supply, correctly
+ totalSupply = _yamToFragment(initSupply);
+
+ emit Rebase(epoch, prevYamsScalingFactor, yamsScalingFactor);
+ return totalSupply;
+ }
+
+ function yamToFragment(uint256 yam)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ return _yamToFragment(yam);
+ }
+
+ function fragmentToYam(uint256 value)
+ override
+ external
+ view
+ returns (uint256)
+ {
+ return _fragmentToYam(value);
+ }
+
+ function _yamToFragment(uint256 yam)
+ internal
+ view
+ returns (uint256)
+ {
+ return (yam * yamsScalingFactor) / internalDecimals;
+ }
+
+ function _fragmentToYam(uint256 value)
+ internal
+ view
+ returns (uint256)
+ {
+ return (value * internalDecimals) / yamsScalingFactor;
+ }
+
+ // Rescue tokens
+ function rescueTokens(
+ address token,
+ address to,
+ uint256 amount
+ )
+ external
+ onlyGov
+ returns (bool)
+ {
+ // transfer to
+ SafeERC20.safeTransfer(IERC20(token), to, amount);
+ return true;
+ }
+}
+
+contract YAMLogic3 is YAMToken {
+ /**
+ * @notice Initialize the new money market
+ * @param name_ ERC-20 name of this token
+ * @param symbol_ ERC-20 symbol of this token
+ * @param decimals_ ERC-20 decimal precision of this token
+ */
+ function initialize(
+ string memory name_,
+ string memory symbol_,
+ uint8 decimals_,
+ address initial_owner,
+ uint256 initTotalSupply_
+ )
+ public
+ {
+ super.initialize(name_, symbol_, decimals_);
+
+ yamsScalingFactor = BASE;
+ initSupply = _fragmentToYam(initTotalSupply_);
+ totalSupply = initTotalSupply_;
+ _yamBalances[initial_owner] = initSupply;
+
+ DOMAIN_SEPARATOR = keccak256(
+ abi.encode(
+ DOMAIN_TYPEHASH,
+ keccak256(bytes(name)),
+ getChainId(),
+ address(this)
+ )
+ );
+ }
+}
+
diff --git a/src/utils/YAMTest.sol b/src/utils/YAMTest.sol
new file mode 100644
index 0000000..f301859
--- /dev/null
+++ b/src/utils/YAMTest.sol
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+import "ds-test/test.sol";
+import "./YAMHelper.sol";
+import "./YAMDelegator.sol";
+import "./VestingPool.sol";
+
+interface Vm {
+ function warp(uint) external;
+ function roll(uint) external;
+ function store(address,bytes32,bytes32) external;
+ function load(address,bytes32) external returns (bytes32);
+}
+
+interface Timelock {
+ function admin() external returns (address);
+ function delay() external returns (uint256);
+}
+
+interface YAMGovernorAlpha {
+ enum ProposalState {
+ Pending,
+ Active,
+ Canceled,
+ Defeated,
+ Succeeded,
+ Queued,
+ Expired,
+ Executed
+ }
+
+ function latestProposalIds(address proposer) external returns (uint256);
+
+ function propose(
+ address[] memory targets,
+ uint[] memory values,
+ string[] memory signatures,
+ bytes[] memory calldatas,
+ string memory description
+ ) external returns (uint256);
+
+ function queue(uint256 proposalId) external ;
+
+ function execute(uint256 proposalId) external payable;
+
+ function castVote(uint256 proposalId, bool support) external;
+
+ function state(uint256 proposalId) external view returns (ProposalState);
+ function getPriorVotes(address account, uint256 blockNumber) external returns (uint256);
+}
+
+interface YAMReserves {}
+
+
+contract YAMTest is YAMHelper {
+ Timelock internal timelock = Timelock(0x8b4f1616751117C38a0f84F9A146cca191ea3EC5);
+ YAMReserves internal reserves = YAMReserves(0x97990B693835da58A281636296D2Bf02787DEa17);
+ YAMDelegator internal yamDelegator = YAMDelegator(0x0AaCfbeC6a24756c20D41914F2caba817C0d8521);
+ VestingPool internal vestingPool = VestingPool(0xDCf613db29E4d0B35e7e15e93BF6cc6315eB0b82);
+ address internal constant yUSDC = address(0x5f18C75AbDAe578b483E5F43f12a39cF75b973a9);
+ address internal constant USDC = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
+ address internal proposer;
+ Vm internal vm;
+
+ function setUpYAMTest() internal {
+ vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
+ proposer = address(this);
+ addKnown(address(yamDelegator), "pendingGov()", 4);
+ addKnown(address(yamDelegator), "totalSupply()", 8);
+ addKnown(address(yamDelegator), "balanceOfUnderlying(address)", 10);
+ addKnown(address(yamDelegator), "initSupply()", 12);
+ addKnown(address(yamDelegator), "checkpoints(address,uint32)", 15);
+ addKnown(address(yamDelegator), "numCheckpoints(address)", 16);
+ // 0 out balance
+ writeBoU(yamDelegator, proposer, 0);
+ }
+
+ function rollProposal(
+ address[] memory targets,
+ uint256[] memory values,
+ string[] memory signatures,
+ bytes[] memory calldatas,
+ string memory description
+ )
+ internal
+ {
+ YAMGovernorAlpha gov = YAMGovernorAlpha(timelock.admin());
+
+ gov.propose(
+ targets,
+ values,
+ signatures,
+ calldatas,
+ description
+ );
+
+ uint256 id = gov.latestProposalIds(proposer);
+
+ voteOnLatestProposal();
+
+ vm.roll(block.number + 12345);
+
+ YAMGovernorAlpha.ProposalState state = gov.state(id);
+
+ assertTrue(state == YAMGovernorAlpha.ProposalState.Succeeded);
+
+ gov.queue(id);
+
+ vm.warp(block.timestamp + timelock.delay());
+
+ gov.execute(id);
+ }
+
+ function voteOnLatestProposal() public {
+ vm.roll(block.number + 10);
+ YAMGovernorAlpha gov = YAMGovernorAlpha(timelock.admin());
+ uint256 id = gov.latestProposalIds(proposer);
+ gov.castVote(id, true);
+ }
+}
diff --git a/src/utils/YAMTokenInterface.sol b/src/utils/YAMTokenInterface.sol
new file mode 100644
index 0000000..08f99d6
--- /dev/null
+++ b/src/utils/YAMTokenInterface.sol
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+import "./YAMTokenStorage.sol";
+import "./YAMGovernanceStorage.sol";
+
+abstract contract YAMTokenInterface is YAMTokenStorage, YAMGovernanceStorage {
+
+ /// @notice An event thats emitted when an account changes its delegate
+ event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
+
+ /// @notice An event thats emitted when a delegate account's vote balance changes
+ event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
+
+ /**
+ * @notice Event emitted when tokens are rebased
+ */
+ event Rebase(uint256 epoch, uint256 prevYamsScalingFactor, uint256 newYamsScalingFactor);
+
+ /*** Gov Events ***/
+
+ /**
+ * @notice Event emitted when pendingGov is changed
+ */
+ event NewPendingGov(address oldPendingGov, address newPendingGov);
+
+ /**
+ * @notice Event emitted when gov is changed
+ */
+ event NewGov(address oldGov, address newGov);
+
+ /**
+ * @notice Sets the rebaser contract
+ */
+ event NewRebaser(address oldRebaser, address newRebaser);
+
+ /**
+ * @notice Sets the migrator contract
+ */
+ event NewMigrator(address oldMigrator, address newMigrator);
+
+ /**
+ * @notice Sets the incentivizer contract
+ */
+ event NewIncentivizer(address oldIncentivizer, address newIncentivizer);
+
+ /* - ERC20 Events - */
+
+ /**
+ * @notice EIP20 Transfer event
+ */
+ event Transfer(address indexed from, address indexed to, uint amount);
+
+ /**
+ * @notice EIP20 Approval event
+ */
+ event Approval(address indexed owner, address indexed spender, uint amount);
+
+ /* - Extra Events - */
+ /**
+ * @notice Tokens minted event
+ */
+ event Mint(address to, uint256 amount);
+
+ /**
+ * @notice Tokens burned event
+ */
+ event Burn(address from, uint256 amount);
+
+ // Public functions
+ function transfer(address to, uint256 value) virtual external returns(bool);
+ function transferFrom(address from, address to, uint256 value) virtual external returns(bool);
+ function balanceOf(address who) virtual external view returns(uint256);
+ function balanceOfUnderlying(address who) virtual external view returns(uint256);
+ function allowance(address owner_, address spender) virtual external view returns(uint256);
+ function approve(address spender, uint256 value) virtual external returns (bool);
+ function increaseAllowance(address spender, uint256 addedValue) virtual external returns (bool);
+ function decreaseAllowance(address spender, uint256 subtractedValue) virtual external returns (bool);
+ function maxScalingFactor() virtual external view returns (uint256);
+ function yamToFragment(uint256 yam) virtual external view returns (uint256);
+ function fragmentToYam(uint256 value) virtual external view returns (uint256);
+
+ /* - Governance Functions - */
+ function getPriorVotes(address account, uint blockNumber) virtual external view returns (uint256);
+ function delegateBySig(address delegatee, uint nonce, uint expiry, uint8 v, bytes32 r, bytes32 s) virtual external;
+ function delegate(address delegatee) virtual external;
+ function delegates(address delegator) virtual external view returns (address);
+ function getCurrentVotes(address account) virtual external view returns (uint256);
+
+ /* - Permissioned/Governance functions - */
+ function mint(address to, uint256 amount) virtual external returns (bool);
+ function burn(uint256 amount) virtual external returns (bool);
+ function rebase(uint256 epoch, uint256 indexDelta, bool positive) virtual external returns (uint256);
+ function _setRebaser(address rebaser_) virtual external;
+ function _setIncentivizer(address incentivizer_) virtual external;
+ function _setPendingGov(address pendingGov_) virtual external;
+ function _acceptGov() virtual external;
+}
diff --git a/src/utils/YAMTokenStorage.sol b/src/utils/YAMTokenStorage.sol
new file mode 100644
index 0000000..39d0ff2
--- /dev/null
+++ b/src/utils/YAMTokenStorage.sol
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.10;
+
+
+// Storage for a YAM token
+contract YAMTokenStorage {
+ /**
+ * @dev Guard variable for re-entrancy checks. Not currently used
+ */
+ bool internal _notEntered;
+
+ /**
+ * @notice EIP-20 token name for this token
+ */
+ string public name;
+
+ /**
+ * @notice EIP-20 token symbol for this token
+ */
+ string public symbol;
+
+ /**
+ * @notice EIP-20 token decimals for this token
+ */
+ uint8 public decimals;
+
+ /**
+ * @notice Governor for this contract
+ */
+ address public gov;
+
+ /**
+ * @notice Pending governance for this contract
+ */
+ address public pendingGov;
+
+ /**
+ * @notice Approved rebaser for this contract
+ */
+ address public rebaser;
+
+ /**
+ * @notice Approved migrator for this contract
+ */
+ address public migrator;
+
+ /**
+ * @notice Incentivizer address of YAM protocol
+ */
+ address public incentivizer;
+
+ /**
+ * @notice Total supply of YAMs
+ */
+ uint256 public totalSupply;
+
+ /**
+ * @notice Internal decimals used to handle scaling factor
+ */
+ uint256 public constant internalDecimals = 10**24;
+
+ /**
+ * @notice Used for percentage maths
+ */
+ uint256 public constant BASE = 10**18;
+
+ /**
+ * @notice Scaling factor that adjusts everyone's balances
+ */
+ uint256 public yamsScalingFactor;
+
+ mapping (address => uint256) internal _yamBalances;
+
+ mapping (address => mapping (address => uint256)) internal _allowedFragments;
+
+ uint256 public initSupply;
+
+
+ // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
+ bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
+ bytes32 public DOMAIN_SEPARATOR;
+}